Vikunja es una aplicación de código abierto para gestionar tareas y proyectos, con vistas de lista, tablero Kanban, tabla y Gantt, sincronización por CalDAV y una API REST completa. Es una alternativa autoalojada a Todoist o Trello. En esta guía desplegarás Vikunja con Docker Compose y PostgreSQL en Ubuntu 24.04, lo publicarás con Nginx y un certificado de Let's Encrypt, cerrarás el registro público y comprobarás la API y CalDAV.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 1 GB de RAM.
  • Un usuario no root con privilegios sudo.
  • Docker Engine y el plugin Docker Compose instalados desde el repositorio oficial de Docker.
  • Un subdominio, por ejemplo tareas.tu_dominio.com, con un registro DNS A apuntando a la IP pública del servidor.
  • Los puertos 80 y 443 accesibles desde Internet.

Sustituye tareas.tu_dominio.com por tu subdominio real.

Paso 1: Preparar directorios y secretos

Desde la versión 0.24, la imagen vikunja/vikunja incluye la API y la interfaz web en un solo contenedor que escucha en el puerto 3456. Crea el directorio del despliegue y los subdirectorios para adjuntos y base de datos:

sudo mkdir -p /opt/vikunja/files /opt/vikunja/db
cd /opt/vikunja

El proceso de Vikunja se ejecuta con el UID 1000 dentro del contenedor, así que el directorio de adjuntos debe pertenecer a ese UID:

sudo chown 1000 /opt/vikunja/files

Genera dos valores aleatorios, uno para la contraseña de PostgreSQL y otro para el secreto con el que Vikunja firma los tokens de sesión:

openssl rand -hex 24
openssl rand -hex 32

Guárdalos en un archivo .env junto al archivo de Compose:

sudo nano /opt/vikunja/.env
DB_PASSWORD=primer_valor_generado
JWT_SECRET=segundo_valor_generado
PUBLIC_URL=https://tareas.tu_dominio.com/
sudo chmod 600 /opt/vikunja/.env

El secreto JWT debe mantenerse estable: si cambia, todas las sesiones abiertas dejan de ser válidas.

Paso 2: Crear el archivo de Docker Compose

sudo nano /opt/vikunja/docker-compose.yml
services:
  vikunja:
    image: vikunja/vikunja:latest
    container_name: vikunja
    restart: unless-stopped
    ports:
      - "127.0.0.1:3456:3456"
    environment:
      VIKUNJA_SERVICE_PUBLICURL: ${PUBLIC_URL}
      VIKUNJA_SERVICE_JWTSECRET: ${JWT_SECRET}
      VIKUNJA_SERVICE_TIMEZONE: Europe/Madrid
      VIKUNJA_SERVICE_ENABLEREGISTRATION: "true"
      VIKUNJA_DATABASE_TYPE: postgres
      VIKUNJA_DATABASE_HOST: db
      VIKUNJA_DATABASE_USER: vikunja
      VIKUNJA_DATABASE_PASSWORD: ${DB_PASSWORD}
      VIKUNJA_DATABASE_DATABASE: vikunja
    volumes:
      - ./files:/app/vikunja/files
    depends_on:
      db:
        condition: service_healthy

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

Algunos detalles importantes:

  • VIKUNJA_SERVICE_PUBLICURL debe ser la URL pública final, con barra al final. Vikunja la usa para los enlaces de los correos y para CalDAV.
  • El registro queda abierto de momento para que puedas crear tu cuenta; lo cerrarás en el Paso 5.
  • La imagen de PostgreSQL se fija en la versión 17. Las versiones mayores de PostgreSQL no pueden abrir directamente un directorio de datos de otra versión, así que conviene no usar latest.
  • El puerto se publica solo en 127.0.0.1: el acceso público lo dará Nginx.

Paso 3: Arrancar Vikunja

sudo docker compose up -d

Comprueba que los dos contenedores están en marcha y que la base de datos aparece como sana:

sudo docker compose ps
NAME         IMAGE                    STATUS
vikunja      vikunja/vikunja:latest   Up 20 seconds
vikunja-db   postgres:17              Up 26 seconds (healthy)

El endpoint /api/v1/info devuelve la versión y la configuración pública del servidor, y sirve para confirmar que la API responde:

curl -s http://127.0.0.1:3456/api/v1/info
{"version":"v1.0.0","frontend_url":"https://tareas.tu_dominio.com/","motd":"","link_sharing_enabled":true,"max_file_size":"20MB","registration_enabled":true, ...}

El número de versión dependerá de la imagen descargada. Si frontend_url no coincide con tu dominio, revisa PUBLIC_URL en .env.

Paso 4: Publicar Vikunja con Nginx y HTTPS

Instala Nginx y Certbot:

sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx

Crea el bloque de servidor:

sudo nano /etc/nginx/sites-available/vikunja
server {
    listen 80;
    listen [::]:80;
    server_name tareas.tu_dominio.com;

    # Tamaño máximo de los adjuntos (Vikunja permite 20 MB por defecto)
    client_max_body_size 20M;

    location / {
        proxy_pass http://127.0.0.1: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;
    }
}

Activa el sitio y recarga Nginx:

sudo ln -s /etc/nginx/sites-available/vikunja /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Si usas UFW, permite SSH y el tráfico web:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Obtén el certificado de Let's Encrypt. Certbot activará HTTPS y la redirección desde HTTP:

sudo certbot --nginx -d tareas.tu_dominio.com

Verifica que la API responde a través del dominio:

curl -s https://tareas.tu_dominio.com/api/v1/info | head -c 80; echo
{"version":"v1.0.0","frontend_url":"https://tareas.tu_dominio.com/","motd":

Paso 5: Crear tu cuenta y cerrar el registro

Abre https://tareas.tu_dominio.com, pulsa Crear cuenta y regístrate con tu usuario, correo y una contraseña robusta. Después crea un proyecto y una tarea de prueba para confirmar que todo funciona. Desde el selector de vistas del proyecto puedes pasar de Lista a Tablero para trabajar en modo Kanban.

Una vez creada tu cuenta, desactiva el registro público para que nadie más pueda darse de alta. Edita docker-compose.yml y cambia la variable:

      VIKUNJA_SERVICE_ENABLEREGISTRATION: "false"

Recrea el contenedor para aplicar el cambio:

sudo docker compose up -d

Comprueba que el registro aparece desactivado:

curl -s https://tareas.tu_dominio.com/api/v1/info | grep -o '"registration_enabled":[a-z]*'
"registration_enabled":false

Con el registro cerrado, puedes dar de alta a otros usuarios desde la línea de comandos. El binario de Vikunja incluye comandos de administración:

sudo docker compose exec vikunja /app/vikunja/vikunja user create -u otro_usuario -e otro_usuario@tu_dominio.com
sudo docker compose exec vikunja /app/vikunja/vikunja user list

El comando user create solicita la contraseña de forma interactiva si no la pasas como opción.

Paso 6: Usar la API con un token

Para scripts e integraciones, en lugar de tu contraseña usa un token de API con permisos limitados. En la web, ve a Ajustes > Tokens de API, crea un token, elige los permisos (por ejemplo, lectura y creación de tareas en proyectos) y una fecha de caducidad, y copia el valor, que empieza por tk_.

Guárdalo en una variable de entorno de tu sesión y lista tus proyectos:

export VIKUNJA_TOKEN='tk_tu_token'
curl -s https://tareas.tu_dominio.com/api/v1/projects \
  -H "Authorization: Bearer $VIKUNJA_TOKEN"

La respuesta es un array JSON con tus proyectos. Anota el id de uno de ellos y crea una tarea en él (Vikunja usa PUT para crear recursos):

curl -s -X PUT https://tareas.tu_dominio.com/api/v1/projects/1/tasks \
  -H "Authorization: Bearer $VIKUNJA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Revisar copias de seguridad", "priority": 3}'

La respuesta incluye la tarea creada con su id, y la verás en la web al recargar el proyecto. La documentación interactiva de todos los endpoints está en https://tareas.tu_dominio.com/api/v1/docs.

Paso 7: Sincronizar tareas por CalDAV

Vikunja expone los proyectos como listas de tareas CalDAV, lo que permite usarlos desde Thunderbird, DAVx5 en Android o Recordatorios en macOS e iOS.

  1. En la web, ve a Ajustes > CalDAV. Ahí se muestra la URL CalDAV de tu cuenta, del tipo https://tareas.tu_dominio.com/dav/principals/tu_usuario/.
  2. Crea un token CalDAV en esa misma página. Úsalo como contraseña en el cliente: es obligatorio si tienes activada la autenticación en dos pasos y recomendable en cualquier caso.
  3. En el cliente, añade una cuenta CalDAV con esa URL, tu nombre de usuario y el token.

Puedes comprobar que el endpoint responde desde el servidor. Debe devolver el código 207:

curl -s -o /dev/null -w '%{http_code}\n' -X PROPFIND -H 'Depth: 1' \
  -u 'tu_usuario:tu_token_caldav' https://tareas.tu_dominio.com/dav/principals/tu_usuario/
207

Paso 8: Copias de seguridad

Los datos de Vikunja están en PostgreSQL y en el directorio de adjuntos. Vuelca la base de datos con pg_dump desde el propio contenedor:

cd /opt/vikunja
sudo docker compose exec -T db pg_dump -U vikunja vikunja | sudo tee /opt/vikunja/vikunja.sql > /dev/null
ls -lh /opt/vikunja/vikunja.sql

Copia vikunja.sql y el directorio /opt/vikunja/files a un almacenamiento externo. Para actualizar Vikunja, ejecuta sudo docker compose pull y sudo docker compose up -d; las migraciones de la base de datos se aplican solas al arrancar.

Solución de problemas

La web carga pero no puedes iniciar sesión, o aparece "Token is invalid". Suele deberse a que JWT_SECRET cambió entre reinicios. Déjalo fijo en .env y vuelve a iniciar sesión.

Error al subir adjuntos. Comprueba que /opt/vikunja/files pertenece al UID 1000 con ls -ln /opt/vikunja y que client_max_body_size en Nginx es al menos igual al tamaño máximo configurado en Vikunja.

Vikunja no arranca y los logs muestran errores de conexión a la base de datos. Revisa ambos servicios:

sudo docker compose logs --tail 50 vikunja
sudo docker compose logs --tail 20 db

Si PostgreSQL rechaza la contraseña, ten en cuenta que POSTGRES_PASSWORD solo se aplica al inicializar ./db por primera vez. Cambiarla después en .env no modifica la contraseña ya guardada.

Conclusión

Tienes Vikunja funcionando con PostgreSQL, publicado con HTTPS, con el registro cerrado y accesible por API y CalDAV. Como siguientes pasos, configura el correo saliente con las variables VIKUNJA_MAILER_* para recibir recordatorios, crea equipos para compartir proyectos con otros usuarios y programa el volcado del Paso 8 con un temporizador de systemd.