GICT knowledge base
How to Self-Host Vikunja with Docker on Ubuntu 24.04
Published
Task lists become more useful when they can hold project notes, due dates, and attachments in one place. Vikunja provides that in a browser, with your tasks stored on your own server.
Before you start
Use a fresh Ubuntu 24.04 VPS with sudo access and capacity for the database, application and attachments. Measure startup and representative use before sizing a production service. Shared hosting without Docker access is not suitable.
This setup suits personal use or a small group whose members already have SSH access. It does not provide a public HTTPS address for colleagues. Email reminders and password-reset messages need an SMTP configuration and are outside this installation guide. The features used here do not require a Vikunja Pro license.
Install Docker and Compose
Inspect the current server first:
$ sudo ss -lntp
$ dpkg -l docker.io docker-ce docker-compose-v2 docker-compose-plugin
Missing packages are normal on a fresh VPS. If Docker is already installed from Docker’s repository, keep that installation and its Compose plugin instead of mixing package sources.
On a fresh Ubuntu installation:
$ sudo apt-get update
$ sudo apt-get install -y docker.io docker-compose-v2 openssl curl
$ sudo systemctl enable --now docker
$ sudo docker version --format '{{.Server.Version}}'
$ sudo docker compose version
The Compose command returned:
Docker Compose version 2.40.3+ds1-0ubuntu1~24.04.1
Use Docker Engine 28 or later and Compose v2. Earlier Docker versions have a localhost port-publishing limitation that can expose a loopback-bound port to other machines on the same network. Update through your existing package source if needed before continuing.
Create the project files
As your normal sudo-capable user:
$ mkdir -p ~/vikunja
$ chmod 700 ~/vikunja
$ cd ~/vikunja
$ mkdir -p files
$ sudo chown 1000 files
$ sudo chmod 750 files
The official Vikunja container runs as UID 1000. Its attachment directory must be writable by that user; database permissions are handled by the PostgreSQL named volume below. This example uses regular Docker Engine, not rootless Docker.
Generate two different random values:
$ openssl rand -hex 32
$ openssl rand -hex 32
Keep them private. Create ~/vikunja/.env, replacing the two REPLACE_ values:
VIKUNJA_VERSION=2.6.0
POSTGRES_PASSWORD=REPLACE_WITH_FIRST_RANDOM_VALUE
VIKUNJA_SECRET=REPLACE_WITH_SECOND_RANDOM_VALUE
The application secret signs authentication tokens; it is not your account password. Protect the file:
$ chmod 600 .env
Create ~/vikunja/compose.yaml:
name: vikunja
services:
db:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_DB: vikunja
POSTGRES_USER: vikunja
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 5s
timeout: 5s
retries: 10
start_period: 30s
vikunja:
image: vikunja/vikunja:${VIKUNJA_VERSION:?Set VIKUNJA_VERSION}
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "127.0.0.1:3456:3456"
environment:
VIKUNJA_SERVICE_PUBLICURL: http://localhost:3456/
VIKUNJA_SERVICE_SECRET: ${VIKUNJA_SECRET:?Set VIKUNJA_SECRET}
VIKUNJA_SERVICE_ENABLEREGISTRATION: "false"
VIKUNJA_DATABASE_TYPE: postgres
VIKUNJA_DATABASE_HOST: db
VIKUNJA_DATABASE_DATABASE: vikunja
VIKUNJA_DATABASE_USER: vikunja
VIKUNJA_DATABASE_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
VIKUNJA_MAILER_ENABLED: "false"
volumes:
- ./files:/app/vikunja/files
volumes:
pgdata:
PostgreSQL has no published port, and Vikunja listens only on the VPS loopback address. PUBLICURL is the address you will open in your browser, not the VPS IP. It includes the local port. The PostgreSQL 18 volume mount is /var/lib/postgresql; do not substitute an older PostgreSQL example’s data path.
Start Vikunja and create your account
From ~/vikunja, validate the configuration without printing its expanded secrets, then start the services:
$ sudo docker compose config --quiet
$ sudo docker compose pull
$ sudo docker compose up -d
$ sudo docker compose ps
$ curl -fsS http://127.0.0.1:3456/health
The health request returned:
OK
The database must become healthy before Vikunja starts. If the HTTP request arrives during initialization, wait a few seconds and run it again. A successful health request confirms the server is answering, but it does not prove that attachments work.
Check the application version:
$ sudo docker compose exec -T vikunja /app/vikunja/vikunja version
A successful account-creation command should confirm the requested user. Output wording can vary by release; inspect the command result before attempting login.
Vikunja api version v2.6.0
Built with go1.27.0
Public registration is disabled. Create your account through the CLI, replacing the example username and email address:
$ sudo docker compose exec vikunja /app/vikunja/vikunja user create --username owner --email you@example.com
Enter a strong, unique password when prompted. Do not put it in the command line. Use an address you control even though email is disabled here. This creates a normal account that owns its projects; it does not require the paid instance-administration panel.
Open the private browser connection
On your own computer, open another terminal and run the following command. Replace ubuntu with your VPS login name and VPS_IP with its address:
$ ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:3456:127.0.0.1:3456 ubuntu@VPS_IP
Use your normal SSH key options if needed. Leave this terminal running. It is normal for the command to show no prompt or output while the tunnel is open.
Open http://localhost:3456/ in your browser and sign in with the account you just created. HTTP runs only on the local ends of this connection; traffic between your computer and VPS is carried inside encrypted SSH. Do not replace the loopback binding with a public port to make access easier.
If local port 3456 is already occupied, close the conflicting local service or choose another local port. If you change it, update VIKUNJA_SERVICE_PUBLICURL to match the new browser address and recreate the Vikunja service with sudo docker compose up -d. Keep the remote destination at port 3456.
Verify tasks and attachments
Select Projects in the sidebar, then New project. Enter Website launch and select Create. Add a task named Review the homepage, open it, add a short description, and set a due date. Select Save below the description. Use Set Due Date, click the new date field, choose a date, and select Confirm. Reload the page to confirm the changes remain.
Select Add Attachments and choose a small text file or PDF. Use the attachment’s download-arrow button (Download this attachment) and open the downloaded copy. Seeing the task save is not enough: project data lives in PostgreSQL, while attachments live in the files directory. In the test, a task still saved when that directory was deliberately made unwritable, but the upload failed. Correcting its ownership restored uploads.
Select Mark task done! and check that Vikunja shows its completed state. Some views hide completed tasks; enable their display if it disappears from the list. Sign out and sign in again to confirm both the account and project remain available.
If something does not work
- Browser cannot connect: confirm that the SSH terminal is still running. On the VPS, run the health request above and
sudo docker compose psfrom~/vikunja. A working server-side response with a failed browser connection points to the tunnel or local port. - Attachment upload fails: check the ownership of
~/vikunja/files. For this fresh installation, UID 1000 needs write access. Do not make the directory world-writable. Rootless Docker has different UID-mapping requirements. - Wrong browser address: keep
VIKUNJA_SERVICE_PUBLICURLconsistent with the exact URL and port you open. Editing a Compose setting requiressudo docker compose up -d; a plain restart does not apply changed environment variables. - Cannot open a shell in the container: the official image has no shell. Run the Vikunja executable directly, as in the account command, or inspect logs:
$ cd ~/vikunja
$ sudo docker compose logs --tail=50 vikunja db
Do not delete the database volume to fix a login or configuration error. The tasks belong to that database, and changing a database password in .env alone does not change an existing PostgreSQL user’s password.
Reference: Vikunja Docker deployment. Check the documentation for your installed version before applying changes.
Related guides
How to Self-Host DocuSeal for Document Signing
Self-host DocuSeal with private administrator setup, PostgreSQL and HTTPS. Test signing, understand email data flows and create consistent backups.
Self HostedHow to Self-Host Docmost with Docker on Ubuntu 24.04
Install Docmost with Docker, PostgreSQL and Redis. Set up a private workspace and verify editing, live updates and attachment retrieval.
Self HostedHow to Remove Image Backgrounds Locally with rembg
Remove image backgrounds locally with rembg. Preserve originals, check PNG transparency and inspect cutout quality on both light and dark backgrounds.
Self HostedHow to Translate Text Offline with Argos Translate
Translate plain text locally with Argos Translate. Install a language pair, prepare caches, verify offline operation and review translation quality.
