bolt Valebyte VPS from $4/mo — NVMe, 60s deploy.

Get a VPS arrow_forward
eco Beginner Tutorial/How-to

Installing Vikunja on a VPS: Self-Hosted Task Manager with Docker, SSL, and Backups

calendar_month Sep 30, 2026 schedule 14 min read visibility 428 views
Установка Vikunja на VPS: self-hosted менеджер задач с Docker, SSL и резервными копиями
info

Need a server for this guide? We offer dedicated servers and VPS in 50+ countries with instant setup.

Need a server for this guide?

Deploy a VPS or dedicated server in minutes.

Installing Vikunja on a VPS: a self-hosted task manager with Docker, SSL, and backups

TL;DR

Vikunja is a self-hosted task manager that can be deployed on a VPS in Docker and used as a private alternative to Trello, Todoist, or ClickUp. This guide will configure Vikunja with PostgreSQL, an HTTPS certificate from Let’s Encrypt via Caddy, basic server protection, and automatic backups.

  • A VPS with 2 vCPU, 2 GB RAM, and 30–40 GB SSD is sufficient for a small team.
  • Vikunja runs in Docker Compose together with PostgreSQL, the frontend, and Caddy.
  • HTTPS is issued automatically after configuring the domain’s DNS record.
  • Passwords and keys are stored in the .env file rather than in the Compose configuration.
  • Backups include a PostgreSQL dump, the Docker configuration, and the Caddy directory.
  • Updates are performed by pulling new images and carrying out a controlled container restart.

What we are configuring and why

Diagram: What we are configuring and why
Diagram: What we are configuring and why

Vikunja is an open-source task and project manager with a web interface, task lists, kanban boards, labels, due dates, recurring tasks, attachments, sharing, and an API. It is suitable for personal planning, small-scale development, internal company processes, and team work without transferring work data to a third-party SaaS service.

In this guide, Vikunja will run on Ubuntu Server 24.04 LTS. The architecture consists of four containers: the Vikunja API, the web frontend, PostgreSQL 17, and Caddy 2.10. Caddy accepts connections from the internet, automatically obtains a TLS certificate from Let’s Encrypt, and proxies requests to Vikunja.

After setup is complete, the service will be available at an address such as https://tasks.example.com. Users will be able to register themselves if this feature is enabled, or the administrator will be able to create the first account and disable public registration.

What you will have as a result

  • Vikunja in Docker Compose without installing application dependencies on the system.
  • PostgreSQL with a dedicated user and a persistent Docker volume.
  • A domain with HTTPS and automatic certificate renewal.
  • Closed internal ports: PostgreSQL and the API are not accessible from the internet.
  • A secrets file excluded from Git and unencrypted backups.
  • A daily encrypted or remote backup of the database and configuration.

Self-hosted or cloud service

Criterion Cloud task manager Vikunja on your own VPS
Data control Data is held by the SaaS provider The database and attachments are stored on your server
Cost Usually charged per user Fixed server and storage costs
Maintenance Updates and backups are handled by the service The administrator is responsible for updates and backups
Integrations Depend on the pricing plan You can use the API, webhooks, and your own automation
Suitable for Teams without a technical administrator Developers, founders, and teams with privacy requirements

The self-hosted option requires discipline: you need to monitor updates, verify backups, and secure SSH access. In return, you gain independence from pricing plans, user limits, and sudden changes to the terms of a cloud product.

What VPS configuration is needed for this task

Diagram: What VPS configuration is needed for this task
Diagram: What VPS configuration is needed for this task

Vikunja itself consumes few resources. PostgreSQL uses most of the memory, while disk space is consumed by the database, attached files, Docker images, logs, and backups. You do not need a powerful server to get started, but an overly small VPS will cause problems when updating images and creating database dumps.

Scenario CPU RAM Disk Network
Personal use, 1–5 users 1 vCPU 1 GB 25 GB SSD 100 Mbps
Small team, 5–30 users 2 vCPU 2–4 GB 40–80 GB NVMe 100 Mbps or 1 Gbps
Team of 30–150 users, many attachments 4 vCPU 8 GB 120 GB NVMe and external backup 1 Gbps

A practical starting option is 2 vCPU, 2 GB RAM, 40 GB NVMe, and a public IPv4 address. This configuration is sufficient for Vikunja, PostgreSQL, Caddy, daily backups, and several dozen active users. For example, you can choose a VPS with the specified characteristics or a similar server from another provider.

When you will need more memory

Increase RAM to 4–8 GB if a Git server, Mattermost, monitoring, CI runners, or other containers are running on the same host. PostgreSQL can use the Linux file cache, so additional memory usually has a more noticeable effect than adding more vCPUs.

When you need dedicated rather than VPS

A dedicated server is not necessary for ordinary Vikunja use. It becomes justified if you store hundreds of gigabytes of attachments, serve hundreds of active users, run several resource-intensive services on one host, or need guaranteed IOPS and dedicated CPUs. In most cases, it is easier to start with a VPS and upgrade the plan without migrating the application.

How to choose a location

Choose a data center closer to your main team: this will reduce latency when opening the interface and uploading attachments. If personal data processing requirements matter, consider the hosting country, your agreement with the provider, and where backups are stored. It is better to keep the backup in another data center or with another provider: a single failure should not destroy both the working data and its copy.

Server preparation

Diagram: Server preparation
Diagram: Server preparation

The instructions below assume a clean server with Ubuntu Server 24.04 LTS and access as the root user. Before installation, create an A DNS record pointing to the server’s IPv4 address. The examples use the domain tasks.example.com; replace it with your own.

Update the system

First, install the latest security updates and basic utilities.

apt update && apt upgrade -y
apt install -y curl ca-certificates gnupg ufw fail2ban unattended-upgrades nano

Reboot the server if the kernel or system libraries were updated.

reboot

Create a separate user

Do not perform daily administration as root. Create a deploy user, add it to the sudo group, and configure SSH key-based login.

adduser deploy
usermod -aG sudo deploy
mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
nano /home/deploy/.ssh/authorized_keys
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh

Insert your public SSH key into the authorized_keys file, such as the contents of ~/.ssh/id_ed25519.pub on your local computer. Test the connection in a second terminal window without closing the current root session.

ssh deploy@SERVER_IP

Disable password-based SSH login

After successfully verifying the key, create a separate SSH configuration file. This is safer than editing the package’s main file.

sudo nano /etc/ssh/sshd_config.d/99-hardening.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3

Check the syntax and restart the SSH service.

sudo sshd -t && sudo systemctl restart ssh

Configure the firewall

Only SSH, HTTP, and HTTPS should remain open. Do not expose PostgreSQL port 5432, the Vikunja API port 3456, or the frontend port 80 inside Docker to the outside.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

Enable Fail2ban and automatic security updates

Fail2ban temporarily blocks IP addresses after suspicious SSH login attempts. Automatic updates close known Ubuntu vulnerabilities without manual intervention.

sudo systemctl enable --now fail2ban
sudo dpkg-reconfigure -plow unattended-upgrades
sudo fail2ban-client status sshd

Before disabling password-based login, make sure the SSH key works. Losing the only private key without provider console access can prevent server administration.

Software Installation — Step by Step

Diagram: Software Installation — Step by Step
Diagram: Software Installation — Step by Step

The deployment uses Docker Engine 28.x or a newer stable release and the Docker Compose plugin. It is better to pin Vikunja images to a specific tag rather than use latest: this prevents an update from happening accidentally during the next launch.

Install Docker from the official repository

Remove potentially conflicting packages if they were installed from the standard Ubuntu repository.

sudo apt remove -y docker.io docker-compose docker-compose-v2 podman-docker containerd runc || true

Add the Docker key and repository.

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update

Install Docker Engine, CLI, Buildx, and the Compose plugin.

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo docker --version
sudo docker compose version

Add the deploy user to the Docker group. After running the command, log out of the SSH session and log in again for the group membership to take effect.

sudo usermod -aG docker deploy
exit

Reconnect and make sure Docker is available without sudo.

ssh deploy@SERVER_IP
docker ps

Create the working directory structure

All service files will be stored in /opt/vikunja. The directory is accessible to the deploy user but not to other local users.

sudo mkdir -p /opt/vikunja/{caddy,data,backups,scripts}
sudo chown -R deploy:deploy /opt/vikunja
chmod 750 /opt/vikunja
cd /opt/vikunja

Check domain availability

Before starting Caddy, the DNS record must already point to the server. Checking from the server itself will show which address the public DNS returns.

getent ahostsv4 tasks.example.com

If the displayed IP is not that of your VPS, correct the DNS and wait for the record to propagate. Incoming ports 80 and 443 must also be available for issuing a Let’s Encrypt certificate.

Vikunja, Docker, and SSL Configuration

Diagram: Vikunja, Docker, and SSL Configuration
Diagram: Vikunja, Docker, and SSL Configuration

Vikunja version 0.24.6 is used below as an example of a pinned stable tag. Before production deployment, check the current stable release in the official Vikunja release notes and replace the variable value if necessary. Do not use an unverified tag simply because it is newer: first create a backup and test the update.

Create an environment variables file

Generate two random secrets: a PostgreSQL password and a Vikunja JWT secret. The password must not contain spaces or characters that may be interpreted incorrectly by the shell.

cd /opt/vikunja
openssl rand -base64 32
openssl rand -hex 32

Create the .env file and insert your values. The VIKUNJA_SERVICE_PUBLICURL value must end with a slash.

nano /opt/vikunja/.env
chmod 600 /opt/vikunja/.env
VIKUNJA_VERSION=0.24.6
POSTGRES_DB=vikunja
POSTGRES_USER=vikunja
POSTGRES_PASSWORD=CHANGE_TO_LONG_RANDOM_DATABASE_PASSWORD
VIKUNJA_SERVICE_JWTSECRET=CHANGE_TO_LONG_RANDOM_JWT_SECRET
VIKUNJA_SERVICE_PUBLICURL=https://tasks.example.com/
VIKUNJA_SERVICE_ENABLEREGISTRATION=true
VIKUNJA_SERVICE_TIMEZONE=Europe/Moscow
VIKUNJA_SERVICE_MAXIMUMATTACHMENTSIZE=20971520
DOMAIN=tasks.example.com

The VIKUNJA_SERVICE_MAXIMUMATTACHMENTSIZE parameter is set in bytes and limits the size of a single attachment to 20 MB. For larger files, increase the limit and account for available disk space. Public registration is usually disabled after the first administrator is created.

Create the Docker Compose file

The Compose file creates an isolated network. Only Caddy ports are exposed to the internet, while PostgreSQL, the API, and the frontend are accessible only to containers within the network.

nano /opt/vikunja/compose.yaml
services:
  db:
    image: postgres:17-alpine
    container_name: vikunja-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - vikunja

  api:
    image: vikunja/api:${VIKUNJA_VERSION}
    container_name: vikunja-api
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      VIKUNJA_DATABASE_TYPE: postgres
      VIKUNJA_DATABASE_HOST: db
      VIKUNJA_DATABASE_USER: ${POSTGRES_USER}
      VIKUNJA_DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
      VIKUNJA_DATABASE_DATABASE: ${POSTGRES_DB}
      VIKUNJA_DATABASE_SSLMODE: disable
      VIKUNJA_SERVICE_JWTSECRET: ${VIKUNJA_SERVICE_JWTSECRET}
      VIKUNJA_SERVICE_PUBLICURL: ${VIKUNJA_SERVICE_PUBLICURL}
      VIKUNJA_SERVICE_ENABLEREGISTRATION: ${VIKUNJA_SERVICE_ENABLEREGISTRATION}
      VIKUNJA_SERVICE_TIMEZONE: ${VIKUNJA_SERVICE_TIMEZONE}
      VIKUNJA_SERVICE_MAXIMUMATTACHMENTSIZE: ${VIKUNJA_SERVICE_MAXIMUMATTACHMENTSIZE}
    volumes:
      - vikunja_files:/app/vikunja/files
    networks:
      - vikunja

  frontend:
    image: vikunja/frontend:${VIKUNJA_VERSION}
    container_name: vikunja-frontend
    restart: unless-stopped
    depends_on:
      - api
    networks:
      - vikunja

  caddy:
    image: caddy:2.10-alpine
    container_name: vikunja-caddy
    restart: unless-stopped
    depends_on:
      - frontend
      - api
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    environment:
      DOMAIN: ${DOMAIN}
    volumes:
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - vikunja

networks:
  vikunja:
    name: vikunja-network

volumes:
  postgres_data:
  vikunja_files:
  caddy_data:
  caddy_config:

Configure Caddy

Caddy will automatically request a Let’s Encrypt certificate, renew it, and redirect HTTP traffic to HTTPS. The frontend receives regular requests, while API requests with the /api/ prefix are routed to the Vikunja API container.

nano /opt/vikunja/caddy/Caddyfile
{$DOMAIN} {
    encode zstd gzip

    @api path /api/
    reverse_proxy @api api:3456

    reverse_proxy frontend:80

    header {
        -Server
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "SAMEORIGIN"
        Referrer-Policy "strict-origin-when-cross-origin"
    }

    log {
        output stdout
        format console
    }
}

Check the resulting Compose configuration without printing secret contents to a public log.

cd /opt/vikunja
docker compose config --quiet

Start the containers

The initial image download may take several minutes. After startup, Vikunja will automatically run the database migrations.

cd /opt/vikunja
docker compose pull
docker compose up -d
docker compose ps

Under normal conditions, the vikunja-db, vikunja-api, vikunja-frontend, and vikunja-caddy containers have the Up status. If the API does not start immediately, check its log: the initial migrations may take some time.

docker compose logs --tail=100 api
docker compose logs --tail=100 caddy

Check HTTPS and the API

Checking from the server confirms that Caddy has served the certificate and that the API responds through the public domain.

curl -I https://tasks.example.com
curl -fsS https://tasks.example.com/api/v1/info

The first request should return code 200 or 308 followed by a redirect to HTTPS. The second usually returns JSON with Vikunja information. Then open the domain in a browser, register the first account, and create a test project.

Disable public registration after creating an administrator

If the service is not intended for open registration, change the value in .env and recreate only the API container. Existing users will still be able to sign in, but new accounts will not be created through the web form.

sed -i 's/VIKUNJA_SERVICE_ENABLEREGISTRATION=true/VIKUNJA_SERVICE_ENABLEREGISTRATION=false/' /opt/vikunja/.env
cd /opt/vikunja
docker compose up -d --force-recreate api

Backups and Maintenance

Diagram: Backups and Maintenance
Diagram: Backups and Maintenance

A Docker volume is not a backup. It is stored on the same disk as the running service, so it does not protect against VPS deletion, administrator error, file system failure, or server compromise. A reliable backup must be separate, created regularly, and periodically verified through restoration.

What needs to be backed up

  • PostgreSQL dump: tasks, users, projects, access rights, and settings.
  • Docker volume vikunja_files: files attached to tasks.
  • The /opt/vikunja directory: Compose, Caddyfile, the .env file, and scripts.
  • Caddy data: certificates and ACME state. They are not critical for Vikunja data, but they speed up recovery.

Below is a simple script that creates a compressed database dump and archives attachment files. For long-term storage, use restic: it encrypts data on the server side and can send it to S3-compatible storage, an SFTP repository, or a separate VPS.

Install restic

sudo apt update
sudo apt install -y restic

For S3-compatible storage, create a file with variables. Do not add it to Git and set permissions to 600.

nano /opt/vikunja/.restic-env
chmod 600 /opt/vikunja/.restic-env
export RESTIC_REPOSITORY="s3:https://s3.example.net/vikunja-backups"
export RESTIC_PASSWORD="CHANGE_TO_A_LONG_UNIQUE_BACKUP_PASSWORD"
export AWS_ACCESS_KEY_ID="S3_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="S3_SECRET_KEY"

Initialize the empty repository once.

source /opt/vikunja/.restic-env
restic init

Create a backup script

The script first creates a consistent PostgreSQL dump using pg_dump, then sends the dump, configuration, and file volume to restic. The Docker command temporarily starts an Alpine container that reads the attachment volume only for archiving.

nano /opt/vikunja/scripts/backup.sh
chmod 700 /opt/vikunja/scripts/backup.sh
#!/usr/bin/env bash
set -euo pipefail

APP_DIR="/opt/vikunja"
BACKUP_DIR="${APP_DIR}/backups"
DATE="$(date +%F_%H-%M-%S)"

source "${APP_DIR}/.restic-env"
mkdir -p "${BACKUP_DIR}"

cd "${APP_DIR}"

docker compose exec -T db pg_dump \
  -U "${POSTGRES_USER}" \
  -d "${POSTGRES_DB}" \
  -Fc > "${BACKUP_DIR}/vikunja_${DATE}.dump"

docker run --rm \
  -v vikunja_vikunja_files:/source:ro \
  -v "${BACKUP_DIR}:/backup" \
  alpine:3.21 \
  tar -czf "/backup/vikunja_files_${DATE}.tar.gz" -C /source .

restic backup \
  "${BACKUP_DIR}/vikunja_${DATE}.dump" \
  "${BACKUP_DIR}/vikunja_files_${DATE}.tar.gz" \
  "${APP_DIR}/compose.yaml" \
  "${APP_DIR}/.env" \
  "${APP_DIR}/caddy/Caddyfile"

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune

rm -f "${BACKUP_DIR}/vikunja_${DATE}.dump"
rm -f "${BACKUP_DIR}/vikunja_files_${DATE}.tar.gz"

Run it manually the first time. This lets you immediately see an S3 access error, an incorrect password, or an issue with the Docker volume name.

/opt/vikunja/scripts/backup.sh
source /opt/vikunja/.restic-env
restic snapshots

Set up daily execution with cron

Run the backup at night when the load is minimal. Output is sent to a log, which should be reviewed periodically.

crontab -e
30 3    /opt/vikunja/scripts/backup.sh >> /opt/vikunja/backups/backup.log 2>&1

Recovery verification

At least once per quarter, restore one backup to a test VPS or a separate directory. The presence of snapshots does not guarantee that the repository password is preserved, the archive is not corrupted, or the recovery procedure is understood in an emergency.

source /opt/vikunja/.restic-env
restic restore latest --target /tmp/vikunja-restore-test
find /tmp/vikunja-restore-test -type f | head

Update plan

For a small installation, update Vikunja during a maintenance window every 1–2 months. First create a backup, read the changelog for the target version, change VIKUNJA_VERSION in .env, pull the images, and recreate the containers. Do not change the PostgreSQL major version by simply changing the tag: it requires a separate data migration procedure.

cd /opt/vikunja
/opt/vikunja/scripts/backup.sh
nano .env
docker compose pull
docker compose up -d
docker compose logs --tail=100 api
curl -fsS https://tasks.example.com/api/v1/info

A rolling update makes sense with multiple API instances and a separate database. For a single VPS, a short controlled interruption is sufficient: the interface is usually unavailable for less than a minute, while the risk of an incorrect update is lower.

Troubleshooting and FAQ

Why is Caddy not obtaining an SSL certificate?

Check that the domain's A DNS record points to the server's public IP and that ports 80 and 443 are allowed in UFW and the provider's network firewall. View the log with docker compose logs caddy. A common cause is that the domain is proxied through a CDN with an unsuitable SSL mode, or that an IPv6 AAAA record points to another server. If IPv6 is not configured, remove the incorrect AAAA record.

Why does the interface open, but the API returns 502 Bad Gateway?

The 502 code means that Caddy cannot connect to the API container. Check statuses with docker compose ps and the log with docker compose logs api. Usually, the API does not start because of an incorrect PostgreSQL password, a missing JWT secret, or incomplete database initialization. Also ensure that the Caddyfile specifies api:3456, not localhost: containers use the internal Docker network.

Why does Vikunja report a PostgreSQL connection error?

First run docker compose logs db and ensure that the database container has healthy status. If the POSTGRES_PASSWORD password was changed after the first PostgreSQL startup, the database will continue to use the old password from the existing volume. Restore the previous password or create a new user manually inside PostgreSQL. Do not delete the database volume as a way to “fix” the error: this will delete all tasks and users.

What is the minimum suitable VPS configuration?

For one person or a few users, 1 vCPU, 1 GB RAM, and 25 GB SSD is sufficient as a minimum. However, a more practical configuration is 2 vCPU, 2 GB RAM, and 40 GB NVMe: it is more comfortable for Docker updates, creating archives, and storing several days of local temporary backups. If large attachments are planned, increase the disk first, then memory.

What should I choose for this task: VPS or dedicated server?

A VPS is almost always sufficient for Vikunja. The application does not require a dedicated physical server when used by a typical team of up to a hundred people. A dedicated server makes sense for high disk performance requirements, a large volume of files, co-hosting heavy services, or the need for guaranteed dedicated resources. Starting with a VPS is easier, cheaper, and usually safer from an administration perspective.

Why does the frontend show an old version or a blank page after an update?

First clear the browser cache or open the service in a private window: the frontend is a static application and may be cached. Then verify that the API and frontend use compatible Vikunja tags in .env. Do not update only one of the two images. Check the frontend and Caddy logs, then run docker compose pull && docker compose up -d.

How can I increase the allowed attachment size?

Change the VIKUNJA_SERVICE_MAXIMUMATTACHMENTSIZE parameter in the .env file. The value is specified in bytes: for example, 104857600 corresponds to 100 MB. After changing it, recreate the API container with docker compose up -d --force-recreate api. Check free space with df -h; large attachments quickly increase the Docker volume size and backup time.

Can Vikunja be moved to another server?

Yes. On the new server, install Docker, create the /opt/vikunja directory, and restore compose.yaml, .env, and the Caddyfile. Then restore the PostgreSQL dump to the new database and extract the file archive into the vikunja_vikunja_files volume. After switching DNS, verify login, projects, and attachments. During migration, it is best to stop the old service to avoid losing tasks created after the final backup.

Conclusions and Next Steps

Diagram: Conclusions and Next Steps
Diagram: Conclusions and Next Steps

Vikunja is now running on a VPS in isolated Docker containers, accessible over HTTPS, and protected by basic firewall rules. Task data is stored in PostgreSQL, while attachment files and configuration are included in the backup plan.

  1. Create test projects, configure team permissions, and disable public registration if it is not needed.
  2. Test backup restoration on a separate machine before the service becomes critical to your work.
  3. As the team grows, move backups to separate storage, configure free-space monitoring, and schedule updates.

Was this guide helpful?

Your feedback helps us improve our guides.

Share this post:

Send this guide to someone who may find it useful.

Telegram VKVK WhatsApp Facebook LinkedIn XX

installing Vikunja on a VPS: self-hosted task manager with Docker, SSL, and backups
support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.