Skip to content

Vikunja

Vikunja manages tasks in kanban boards, lists, Gantt and table views. Two containers — a single binary serving both API and interface, and PostgreSQL.

Last run on a host 8 Nov 2026 · data restored from a backup

Pinned at 2.8.0 (released 5 Nov 2026) · the newest upstream release

In the upstream images: 1 CRITICAL · 25 HIGH with a published fix — per image

To try it: Docker, and the same build step, which the local stack runs too. See Try it locally.

To deploy it behind the proxy: Traefik running and a domain, and a build step — the image is built locally from source.

On your own machine, with no proxy, domain or certificate. From business/vikunja/:

Terminal window
cp .env.local.example .env.local
# The secrets ship empty; generate the ones this stack needs
{
echo "DB_PASSWORD=$(openssl rand -base64 32 | tr -d '\n')"
echo "JWT_KEY=$(openssl rand -base64 48 | tr -d '\n')"
} >> .env.local
docker compose -f docker-compose.local.yml --env-file .env.local build
docker compose -f docker-compose.local.yml --env-file .env.local up -d

Then open http://localhost:3456. Stop it again with the same command and down.

From business/vikunja/:

  1. Copy the environment file and set APP_TRAEFIK_HOST and TZ:

    Terminal window
    cp .env.example .env
  2. Generate the secrets. The last two are placeholders that have to exist even when the features are off:

    Terminal window
    mkdir -p .secrets
    openssl rand -base64 48 | tr -d '\n' > .secrets/jwt_key.txt
    openssl rand -base64 32 | tr -d '\n' > .secrets/db_pwd.txt
    printf 'placeholder' > .secrets/oidc_secret.txt
    printf 'placeholder' > .secrets/smtp_pwd.txt
  3. Build the image:

    Terminal window
    docker compose build
  4. Start it:

    Terminal window
    docker compose up -d
  5. Open https://<APP_TRAEFIK_HOST> and register the first account.

Terminal window
docker compose ps # two services, healthy

Then create a project, add a task and attach a file. The attachment lives in a separate volume from the database, so it is the part a database-only backup would miss.

Vikunja can authenticate against Authentik. Two things are easy to miss: the application in Authentik needs at least one group or user binding, or the login endpoint answers 403; and the container has to be able to resolve the Authentik hostname, which is automatic with public DNS and needs a host entry on a server without it.

Replace the placeholder in .secrets/oidc_secret.txt when enabling it.

Vikunja’s built-in vikunja healthcheck command starts without the database password and therefore always fails. The stack checks the running server over HTTP instead.

Data lives in Docker named volumes, not under the stack directory. Their host paths are /var/lib/docker/volumes/vikunja_<name>/_data.

Database PostgreSQL, container vikunja-db, database vikunja, user vikunja
Password .secrets/db_pwd.txt
State db_data (database) · vikunja_files (task attachments)
Also needed .secrets/jwt_key.txt
Quiescing Not needed — the dump is consistent on its own.

For borgmatic:

postgresql_databases:
- name: vikunja
username: vikunja
# The server's own client, inside its container
pg_dump_command: docker exec vikunja-db pg_dump
pg_restore_command: docker exec -i vikunja-db pg_restore
psql_command: docker exec -i vikunja-db psql

jwt_key.txt signs the session tokens. Restoring without it logs everyone out — recoverable, but it happens without explanation.

Restore order: database, then the application.

  1. Read the release notes.

  2. Back up the database, the attachment volume and the key together.

  3. Raise APP_TAG in .env, then rebuild — the local layer has to be applied to the new upstream image:

    Terminal window
    docker compose build --pull
    docker compose up -d
  4. Open a project with an attachment.

Login through Authentik returns 403. The application in Authentik has no group or user binding.

The container cannot reach Authentik. It cannot resolve the hostname. Add it to the Docker host’s /etc/hosts where public DNS does not cover it.

docker compose up fails with a missing image. The build step was skipped. This stack does not pull a ready-made image.

Everyone is logged out after a restore. jwt_key.txt does not match.