Self-Host Vikunja

What Is Vikunja?

Vikunja is an open-source task and project management platform written in Go with a Vue.js frontend. It covers the feature set you would expect from Todoist, Trello, or Microsoft To Do — task lists, Kanban boards, Gantt charts, CalDAV sync, reminders, labels, priorities, assignees, file attachments, and more — running entirely on your own hardware with no cloud dependency.

Since v0.22, Vikunja ships as a single Docker container (vikunja/vikunja) that bundles the API and the web frontend. It uses SQLite by default and optionally supports PostgreSQL or MySQL for larger deployments. For anyone looking for a self-hosted task manager that does not compromise on features, Vikunja is the strongest option available.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker Engine and Docker Compose v2 installed (guide)
  • 512 MB RAM minimum (1 GB recommended with PostgreSQL)
  • 1 GB free disk space for the application and database
  • A domain name (optional, but required for CalDAV and mobile apps over the internet)

Docker Compose Configuration

This setup runs Vikunja with PostgreSQL for reliable, production-grade storage. Vikunja exposes its web UI and API on port 3456.

Create a project directory and prepare the files volume:

mkdir -p /opt/vikunja/files /opt/vikunja/db
chown 1000 /opt/vikunja/files

Vikunja runs as UID 1000 inside the container. The files directory must be writable by that user — this is where task attachments and project backgrounds are stored.

Create a .env file with your secrets:

# /opt/vikunja/.env

# Public URL where users will access Vikunja.
# MUST include the protocol and trailing slash.
# Used for CORS, email links, and CalDAV discovery.
VIKUNJA_PUBLIC_URL=https://tasks.example.com/

# JWT secret — used to sign authentication tokens.
# Generate one: openssl rand -base64 32
VIKUNJA_JWT_SECRET=CHANGE_ME_GENERATE_A_RANDOM_SECRET

# PostgreSQL credentials — must match between Vikunja and the database.
POSTGRES_USER=vikunja
POSTGRES_PASSWORD=CHANGE_ME_USE_A_STRONG_PASSWORD
POSTGRES_DB=vikunja

Create docker-compose.yml:

# /opt/vikunja/docker-compose.yml

services:
  vikunja:
    image: vikunja/vikunja:v1.1.0
    container_name: vikunja
    environment:
      # Service configuration
      VIKUNJA_SERVICE_PUBLICURL: ${VIKUNJA_PUBLIC_URL}
      VIKUNJA_SERVICE_JWTSECRET: ${VIKUNJA_JWT_SECRET}

      # Database configuration
      VIKUNJA_DATABASE_TYPE: postgres
      VIKUNJA_DATABASE_HOST: vikunja-db
      VIKUNJA_DATABASE_USER: ${POSTGRES_USER}
      VIKUNJA_DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
      VIKUNJA_DATABASE_DATABASE: ${POSTGRES_DB}

      # Timezone — affects reminder delivery and timestamps
      TZ: Etc/UTC
    ports:
      - "3456:3456"
    volumes:
      # Task attachments, project backgrounds, and uploaded files
      - ./files:/app/vikunja/files
    depends_on:
      vikunja-db:
        condition: service_healthy
    restart: unless-stopped

  vikunja-db:
    image: postgres:16-alpine
    container_name: vikunja-db
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      # PostgreSQL data directory
      - ./db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -h localhost -U $$POSTGRES_USER"]
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 30s
    restart: unless-stopped

Start the stack:

cd /opt/vikunja
docker compose up -d

Vikunja will be available at http://your-server-ip:3456 once both containers are healthy. First startup takes 10-20 seconds while the database schema is created.

Initial Setup

Open http://your-server-ip:3456 in a browser. You will see the Vikunja login page.

  1. Create an account. Click “Register” and create your first user. The first registered user gets admin privileges automatically.
  2. Create a project. After login, click the plus icon to create your first project. Choose between a list view (default), Kanban board, or Gantt chart.
  3. Add tasks. Type a task title and press Enter. Vikunja supports natural language parsing — type “Buy groceries tomorrow at 5pm” and it will set the due date and time automatically.
  4. Disable open registration (recommended). Once all your users have accounts, go to Settings > General and disable public registration to prevent strangers from signing up.

Configuration

CalDAV Sync

Vikunja exposes a CalDAV endpoint at /dav for syncing tasks with calendar and task apps.

The CalDAV URL for your instance is:

https://tasks.example.com/dav/principals/YOUR_USERNAME/

To connect a CalDAV client:

  1. Use your Vikunja instance URL with the /dav path as the server address
  2. Authenticate with your Vikunja username and password
  3. Your projects appear as separate task lists

Compatible clients: DAVx5 + OpenTasks (Android), Evolution (Linux), KOrganizer (KDE). Note that Thunderbird and iOS native CalDAV have known compatibility issues — use a dedicated task app instead.

Email Notifications

Add these environment variables to the vikunja service to enable email reminders and notifications:

VIKUNJA_MAILER_ENABLED: "true"
VIKUNJA_MAILER_HOST: smtp.example.com
VIKUNJA_MAILER_PORT: "587"
VIKUNJA_MAILER_USERNAME: your-smtp-user
VIKUNJA_MAILER_PASSWORD: your-smtp-password
VIKUNJA_MAILER_FROMEMAIL: [email protected]

With email enabled, Vikunja sends task reminders, due date notifications, and team assignment alerts.

OpenID Connect / SSO

OIDC configuration requires a config file rather than environment variables. Mount a config file into the container:

  1. Create /opt/vikunja/config.yml:
auth:
  openid:
    enabled: true
    providers:
      my-provider:
        name: "Authentik"
        authurl: https://auth.example.com/application/o/vikunja/
        clientid: your-client-id
        clientsecret: your-client-secret
        scopes:
          - openid
          - profile
          - email
  1. Add the volume mount to the vikunja service in docker-compose.yml:
volumes:
  - ./files:/app/vikunja/files
  - ./config.yml:/app/vikunja/config.yml:ro

Vikunja supports any standard OIDC provider including Authentik, Keycloak, and Authelia. New users are created automatically on first OIDC login.

File Uploads

Vikunja stores uploaded files (task attachments, project backgrounds) in the /app/vikunja/files volume by default. You can configure S3-compatible storage instead:

VIKUNJA_FILES_TYPE: s3
VIKUNJA_FILES_S3_ENDPOINT: s3.amazonaws.com
VIKUNJA_FILES_S3_BUCKET: vikunja-files
VIKUNJA_FILES_S3_REGION: us-east-1

The maximum file upload size defaults to 20 MB. Change it with VIKUNJA_FILES_MAXSIZE (value in bytes).

Mobile Apps

Vikunja has native apps for both platforms:

  • Android: Available on F-Droid and the Google Play Store. Search “Vikunja”.
  • iOS: Available on the Apple App Store.

To connect the mobile app, enter your instance URL (e.g., https://tasks.example.com) and log in with your credentials. The app requires HTTPS with a valid certificate for remote connections — a reverse proxy with Let’s Encrypt handles this.

CalDAV sync through DAVx5 + OpenTasks on Android is the alternative if you prefer not to use the native app.

Reverse Proxy

Running Vikunja behind a reverse proxy gives you HTTPS, a clean domain name, and removes the port number from the URL. Make sure VIKUNJA_SERVICE_PUBLICURL in your .env matches the public domain.

Caddy (simplest option):

tasks.example.com {
    reverse_proxy localhost:3456
}

Nginx:

server {
    listen 80;
    server_name tasks.example.com;

    location / {
        proxy_pass http://localhost:3456;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        client_max_body_size 20M;
    }
}

If you increase the max upload size in Vikunja’s settings, you must also update client_max_body_size in Nginx to match.

Nginx Proxy Manager: Create a new Proxy Host pointing to vikunja on port 3456. Enable “Websockets Support”. Request an SSL certificate through the built-in Let’s Encrypt integration.

For the full reverse proxy setup including SSL, see Reverse Proxy Setup.

Backup

Vikunja stores data in two places that need backing up:

  1. PostgreSQL database — contains all projects, tasks, users, labels, and settings.
  2. Files directory — contains task attachments and project background images.

Database Backup

docker exec vikunja-db pg_dumpall -U vikunja > /opt/vikunja/backups/vikunja-db-$(date +%Y%m%d).sql

Files Backup

tar -czf /opt/vikunja/backups/vikunja-files-$(date +%Y%m%d).tar.gz -C /opt/vikunja files/

Restore

To restore the database:

docker exec -i vikunja-db psql -U vikunja < /opt/vikunja/backups/vikunja-db-YYYYMMDD.sql

To restore files, extract the tarball back to /opt/vikunja/files/ and ensure UID 1000 ownership with chown -R 1000 /opt/vikunja/files.

Schedule daily backups with cron and keep copies off-server. See Backup Strategy for the full 3-2-1 backup approach.

Troubleshooting

Permission Denied on Files Directory

Symptom: Vikunja logs show permission errors when uploading attachments. The UI shows a generic upload failure.

Fix: The container runs as UID 1000. Set ownership on the host:

chown -R 1000 /opt/vikunja/files

Vikunja uses a scratch-based image with no shell, so you cannot exec into the container. All permission fixes happen on the host.

CORS Errors in Browser Console

Symptom: The web UI loads but API requests fail with CORS errors. Tasks do not appear.

Fix: VIKUNJA_SERVICE_PUBLICURL must exactly match the URL you access Vikunja from, including protocol and trailing slash:

VIKUNJA_SERVICE_PUBLICURL: https://tasks.example.com/

If the URL is wrong or missing, Vikunja’s CORS headers will not match browser requests. Restart the container after changing this value.

Database Connection Refused on Startup

Symptom: Vikunja exits immediately with “connection refused” pointing to the database host.

Fix: Verify the depends_on condition is set to service_healthy. The healthcheck ensures PostgreSQL is ready before Vikunja tries to connect. If you removed the healthcheck, add it back — Vikunja does not retry database connections on its own.

Also check that VIKUNJA_DATABASE_HOST matches the database service name in your Compose file (e.g., vikunja-db).

CalDAV Not Discovering Projects

Symptom: CalDAV client connects but shows no task lists.

Fix: Use the full principal URL format: https://tasks.example.com/dav/principals/YOUR_USERNAME/. Some clients need the trailing slash. If your reverse proxy strips paths, make sure /dav is proxied correctly without rewriting the path.

Migration From SQLite to PostgreSQL

Symptom: You started with the default SQLite setup and want to move to PostgreSQL for better performance.

Fix: Vikunja does not include a built-in migration tool. Export your data using the Vikunja API (/api/v1/tasks and /api/v1/projects), set up a fresh PostgreSQL instance with the Docker Compose above, create your account, and re-import. For small instances, recreating the data manually may be faster.

Resource Requirements

  • RAM: ~50 MB idle (Vikunja only), ~200 MB total with PostgreSQL under light use
  • CPU: Very low. The Go backend is efficient — a single core handles hundreds of users
  • Disk: ~100 MB for the application image, plus space for file attachments and the database

Vikunja is one of the lightest self-hosted project management tools available. It runs comfortably on a Raspberry Pi 4 or a $5/month VPS.

Verdict

Vikunja is the best self-hosted task management tool for people who want a single app that handles lists, Kanban boards, Gantt charts, and CalDAV sync. It replaced Todoist for me, and the single-container Docker deployment makes it trivial to run.

The native mobile apps are functional if basic — they get the job done for capturing tasks on the go. CalDAV sync through DAVx5 on Android is the more powerful option if you want tasks integrated into your existing calendar workflow.

Where Vikunja falls short: the CalDAV implementation is still maturing and has compatibility issues with some clients (notably iOS and Thunderbird). If CalDAV is your primary interface, test it with your specific client before committing. The web UI and mobile apps work without any issues.

For teams, Vikunja supports shared projects, assignees, and labels — enough for small teams. If you need full-blown project management with time tracking and complex workflows, look at Planka or Focalboard. For personal task management, Vikunja is the clear winner.