Goodbye Todoist, Hello Vikunja
What is Vikunja?
As a longtime user of Todoist, I have been eagerly awaiting an open-source and self-hosted alternative with all the features that I like. So far, it looks like Vikunja will be that replacement!
Vikunja allows me to track tasks on my dashboard and create projects that team members can access, all accessible through an easy dashboard. I use a PWA and CalDAV to access my tasks on mobile. I’d prefer a native app, but this works for now.
Deploying Vikunja
My Vikunja deployment depends on two servers in my homelab rack. Lavender is an Ubuntu server that handles incoming connections as an Nginx reverse proxy and Mint is a Rocky server that hosts several services including Vikunja.
Mint Configuration
Thanks to Vikunja’s fairly thorough Docker installation guide it was easy to deploy using the compose.yml
I’ve included below. The configuration I settled on allows users to perform password resets and configure email reminders for tasks.
version: "3"
services:
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: <secret>
POSTGRES_USER: <secret>
volumes:
- ./db:/var/lib/postgresql/data
restart: unless-stopped
api:
image: vikunja/api
environment:
VIKUNJA_DATABASE_HOST: db
VIKUNJA_DATABASE_PASSWORD: <secret>
VIKUNJA_DATABASE_TYPE: postgres
VIKUNJA_DATABASE_USER: <secret>
VIKUNJA_DATABASE_DATABASE: vikunja
VIKUNJA_SERVICE_JWTSECRET: <secret>
VIKUNJA_SERVICE_FRONTENDURL: https://todo.beans.team/
VIKUNJA_SERVICE_ENABLETASKATTACHMENTS: 1
VIKUNJA_SERVICE_ENABLEREGISTRATION: 0
VIKUNJA_SERVICE_ENABLEEMAILREMINDERS: 1
VIKUNJA_MAILER_ENABLED: 1
VIKUNJA_MAILER_FORCESSL: 1
VIKUNJA_MAILER_HOST: <secret>
VIKUNJA_MAILER_PORT: <secret>
VIKUNJA_MAILER_USERNAME: <secret>
VIKUNJA_MAILER_PASSWORD: <secret>
VIKUNJA_MAILER_FROMEMAIL: <secret>
ports:
- 3456:3456
volumes:
- ./files:/app/vikunja/files
depends_on:
- db
restart: unless-stopped
frontend:
image: vikunja/frontend
restart: unless-stopped
proxy:
image: nginx
ports:
- 4321:80
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- api
- frontend
restart: unless-stopped
The only snag I hit on the way was attempting to handle the Nginx configuration on Lavender which caused issues with my firewall configuration. I settled on using Vikunja’s nginx.conf
within the compose script to handle connections in the way that the application was expecting.
server {
listen 80;
location / {
proxy_pass http://frontend:80;
}
location ~* ^/(api|dav|\.well-known)/ {
proxy_pass http://api:3456;
client_max_body_size 20M;
}
}
Lavender Configuration
After configuring my DNS settings with my domain provider I deployed a new site configuration in Nginx to direct incoming traffic from my todo.
subdomain to the correct port on Mint. Once I was able to validate the new configuration I used Certbot to issue a new certificate for the subdomain to enable HTTPS.
server {
server_name todo.beans.team;
location / {
proxy_pass http://mint:4321;
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;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/todo.beans.team/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/todo.beans.team/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
Vikunja CLI
Since I disabled self registration adding new users requires that I use the Vikunja CLI, so here are some commands that I found useful. When using docker these have to be executed from the API container.
docker exec vikunja-api-1 ./vikunja subcommand
User List
Outputs a table of all registered users.
vikunja user list
User Create
Registers a new user.
vikunja user create -e user@email.com -u username
After running this command you will be prompted to enter a password.
Dump
Creates a zip file of all Vikunja data including the full database.
vikunja dump
There are many more, but you can find them in the official documentation.