Self Hosting Project Management Systems · FrankBoard

How to Set Up a Professional Work Board Using Docker

A production-ready work board can be deployed on any VPS in under ten minutes using Docker Compose with persistent volumes, a reverse proxy for TLS termination, and a managed database. FrankBoard ships as a single container image with PostgreSQL support, making it straightforward to run on Ubuntu, Debian, or any Linux host with Docker Engine installed.

How to Set Up a Professional Work Board Using Docker

What You Need Before Starting

You need a VPS with at least 1 GB RAM and 20 GB SSD, Docker Engine 24.0 or newer, and Docker Compose. A domain name pointed at your server simplifies TLS configuration. FrankBoard and similar self-hosted Kanban tools require these baseline resources to run reliably for small teams.

Step 1: Prepare Your Server

Update your system packages and install Docker using the official repository. On Ubuntu:

sudo apt update && sudo apt install -y ca-certificates curl gnupg
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
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 && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Add your user to the docker group to run commands without sudo:

sudo usermod -aG docker $USER
newgrp docker

Step 2: Configure Persistent Storage

Create a directory structure for volumes that survive container restarts and redeployments:

mkdir -p ~/frankboard/{data,postgres,ssl}
cd ~/frankboard

This separation keeps application data, database files, and certificates organized. FrankBoard stores uploaded files and configuration in /var/www/app/data, which you mount to the local data directory.

Step 3: Write the Compose File

Create docker-compose.yml with explicit version pinning and health checks:

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: frankboard
      POSTGRES_USER: fbuser
      POSTGRES_PASSWORD: your-strong-password-here
    volumes:
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U fbuser -d frankboard"]
      interval: 5s
      timeout: 5s
      retries: 5

  app:
    image: frankboard/frankboard:latest
    environment:
      DATABASE_URL: postgres://fbuser:your-strong-password-here@db:5432/frankboard
    volumes:
      - ./data:/var/www/app/data
    ports:
      - "127.0.0.1:8080:80"
    depends_on:
      db:
        condition: service_healthy

  proxy:
    image: traefik:v3.0
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=admin@yourdomain.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./ssl:/letsencrypt
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frankboard.rule=Host(`board.yourdomain.com`)"
      - "traefik.http.routers.frankboard.entrypoints=websecure"
      - "traefik.http.routers.frankboard.tls.certresolver=letsencrypt"
      - "traefik.http.services.frankboard.loadbalancer.server.port=80"

The 127.0.0.1:8080 binding prevents direct external access to the application container. Traefik handles TLS automatically via Let's Encrypt.

Step 4: Deploy and Verify

Start the stack:

docker compose up -d

Check container health:

docker compose ps
docker compose logs -f app

Once the database migrations complete, navigate to https://board.yourdomain.com. The first login uses default credentials that you must change immediately through the web interface.

Step 5: Harden the Installation

Professional deployments require three security layers beyond basic Docker setup.

Database isolation: The PostgreSQL container exposes no host ports. Communication happens only on the internal Docker network. Rotate passwords with docker compose exec db psql -U fbuser -d frankboard -c "ALTER USER fbuser WITH PASSWORD 'new-password';" then update DATABASE_URL and restart.

Automated backups: Add a backup service to your Compose file or run a cron job:

0 3 * * * docker exec frankboard-db-1 pg_dump -U fbuser frankboard | gzip > ~/backups/frankboard-$(date +\%Y\%m\%d).sql.gz

Firewall rules: Restrict SSH to your IP, allow only 80/443 inbound:

sudo ufw default deny incoming
sudo ufw allow from YOUR_IP to any port 22
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Step 6: Maintain and Update

Update FrankBoard without data loss by pulling the latest image and recreating containers:

cd ~/frankboard
docker compose pull
docker compose up -d

Database schema migrations run automatically on startup. FrankBoard follows semantic versioning, so patch and minor updates apply cleanly. Review release notes before major version jumps.

For rollback protection, snapshot your postgres and data directories before updates:

sudo tar czf frankboard-backup-$(date +%Y%m%d).tar.gz ~/frankboard/postgres ~/frankboard/data

Why Self-Hosting Beats SaaS for Small Teams

Cloud project management tools extract ongoing costs and retain control over your data. A Docker-based work board runs at fixed infrastructure cost, exports data in standard formats, and continues functioning even if the upstream project pauses development. FrankBoard preserves this independence while adding a contemporary interface to the proven Kanboard foundation.

Key Takeaways

Original resource: Visit the source site