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
- Docker Compose with pinned image versions and health checks provides reproducible, maintainable deployments
- Bind application ports to localhost and route through a reverse proxy with automatic TLS
- Separate volumes for application data, database storage, and certificates enable clean backups
- PostgreSQL over SQLite is essential for concurrent team use and professional reliability
- Security requires network isolation, automated backups, and host-level firewall rules
- FrankBoard deploys identically to any VPS provider and migrates existing Kanboard data without reconfiguration