How to Set Up a Work Board on a VPS for Maximum Privacy
A production-grade self-hosted work board on a VPS requires three security layers: SSH key authentication to eliminate password-based entry points, UFW firewall rules to expose only essential ports, and a reverse proxy with TLS termination to encrypt all traffic and obscure the application server from direct internet exposure.
How to Set Up a Work Board on a VPS for Maximum Privacy
Why Self-Hosting Demands a Security-First Approach
Self-hosting project management software puts you in control of your data, but that control carries responsibility. Unlike SaaS platforms that manage infrastructure security, a VPS-based deployment requires you to harden every layer from the operating system up to the application. The payoff is substantial: complete data sovereignty, no third-party access, and immunity to vendor policy changes or shutdowns.
FrankBoard and similar self-hosted Kanban tools ship with sensible defaults, but those defaults assume a protected network. Exposing any application directly to the internet without hardening is a liability. The following architecture treats privacy as a system property built through layered defenses.
Securing Server Access with SSH Keys
Password-based SSH authentication is the most common entry point for automated attacks. Eliminating it is the single highest-impact hardening step.
Generate an Ed25519 key pair on your local machine if you haven't already:
ssh-keygen -t ed25519 -C "[email protected]"
Copy the public key to your VPS:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-vps-ip
Then modify /etc/ssh/sshd_config on the server:
- Set
PasswordAuthentication no - Set
PermitRootLogin no - Set
PubkeyAuthentication yes - Optionally change the SSH port from 22 to a non-standard port above 1024
Restart the SSH service and verify key-only access in a new terminal before closing your existing session. This prevents lockouts during testing.
For additional protection, configure a hardware security key or use SSH certificates if your team size warrants centralized key management. Store your private key in a hardware token or encrypted vault—never on shared development machines.
Hardening the Network Perimeter with UFW
Uncomplicated Firewall (UFW) provides a manageable interface to iptables. Default-deny policies are essential: block everything, then explicitly permit required traffic.
After SSH key setup, enable UFW with these rules:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2222/tcp # Your custom SSH port
sudo ufw allow 80/tcp # HTTP for certificate validation
sudo ufw allow 443/tcp # HTTPS for secured application access
sudo ufw enable
Do not expose application ports directly. FrankBoard and comparable tools typically run on internal ports like 8080 or 5000—these should remain bound to localhost or a private Docker network, unreachable from the internet.
For teams with static office IPs, restrict SSH access further:
sudo ufw allow from 203.0.113.0/24 to any port 2222 proto tcp
This geographic or network-based restriction adds defense even if keys are compromised.
Deploying FrankBoard with Docker and PostgreSQL
Containerization simplifies dependency management and enables network isolation. FrankBoard's Docker deployment separates the application from the database on distinct networks.
Create a docker-compose.yml structure:
version: '3.8'
networks:
frontend:
driver: bridge
backend:
internal: true # No external access
services:
db:
image: postgres:15-alpine
networks:
- backend
environment:
POSTGRES_USER: frankboard
POSTGRES_PASSWORD: [generate-strong-password]
POSTGRES_DB: frankboard
volumes:
- postgres_data:/var/lib/postgresql/data
app:
image: frankboard/frankboard:latest
networks:
- frontend
- backend
environment:
DATABASE_URL: postgresql://frankboard:[password]@db:5432/frankboard
depends_on:
- db
# No ports exposed to host—reverse proxy connects via Docker network
volumes:
postgres_data:
The critical privacy element: the app service publishes no host ports. Communication flows exclusively through the frontend network to your reverse proxy, while the database remains on the isolated backend network.
Generate database credentials with a password manager or openssl rand -base64 32. Rotate these quarterly or on team member departures.
Implementing a Reverse Proxy with TLS Termination
A reverse proxy serves as the controlled entry point, handling TLS encryption, request routing, and additional hardening headers. Two solid options exist: Nginx and Traefik.
Nginx Configuration
Nginx excels when you need explicit control over every directive. Install the Nginx package, then create a site configuration:
server {
listen 80;
server_name board.yourdomain.com;
return 301 https://$server_name$request_uri; # Force HTTPS
}
server {
listen 443 ssl http2;
server_name board.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/board.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/board.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
# Modern security headers
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
proxy_pass http://frankboard_app:8080; # Docker service name
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_hide_header X-Powered-By;
}
}
Obtain certificates via Certbot with DNS validation or HTTP-01 challenge. DNS validation is preferable for privacy as it doesn't require temporary exposure of port 80.
Traefik Configuration
Traefik suits Docker-centric workflows with automatic service discovery. Its docker-compose integration reduces manual configuration:
services:
traefik:
image: traefik:v2.10
command:
- "--api.dashboard=false"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@yourdomain.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
networks:
- frontend
app:
# ... FrankBoard configuration
labels:
- "traefik.enable=true"
- "traefik.http.routers.frankboard.rule=Host(`board.yourdomain.com`)"
- "traefik.http.routers.frankboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.frankboard.entrypoints=websecure"
Traefik automatically requests and renews certificates. The Docker socket mount requires careful permissions—never expose it to untrusted containers.
Enforcing Additional Application-Level Protections
Network hardening protects the perimeter; application controls protect against credential abuse and session hijacking.
Authentication hardening for FrankBoard: - Enforce strong passwords via the administrative settings - Enable two-factor authentication if available - Review active sessions regularly and terminate unknown entries - Create role-based access: administrators manage configuration, members access only relevant projects
Session management:
- Configure short session timeouts for shared workstations
- Use httpOnly and secure cookie flags (handled automatically when X-Forwarded-Proto is set correctly)
Audit logging: - Mount the application logs directory to persistent storage - Forward logs to a centralized system or SIEM for anomaly detection - Review failed login patterns weekly
Maintaining Privacy Through Ongoing Hygiene
Security at setup degrades without maintenance. Establish these practices:
Automated updates: Subscribe to FrankBoard release notifications and PostgreSQL security advisories. Test updates in a staging environment before production deployment. Docker image updates apply with docker-compose pull && docker-compose up -d.
Backup encryption: Encrypt database dumps before off-site storage. Use restic, BorgBackup, or similar tools with client-side encryption. Store encryption keys separately from backup destinations.
Certificate monitoring: TLS certificates expire. Automated renewal fails silently if DNS or validation endpoints change. Monitor with simple health checks or certificate transparency logs.
Network monitoring: Install fail2ban to block repeated SSH or HTTP probing. Review UFW logs monthly for patterns suggesting targeted attention.
Principle of least privilege: Each team member receives exactly the access required. Remove accounts promptly on departure. Rotate shared service credentials quarterly.
Key Takeaways
- SSH key authentication with disabled password logins eliminates the most common server compromise vector
- UFW default-deny policies with explicit port allowances create a minimal network attack surface
- Docker internal networks isolate the database from any external reach
- Reverse proxies (Nginx or Traefik) handle TLS termination, security headers, and request routing without exposing application ports
- Application-level controls including strong authentication, session management, and audit logging provide defense in depth
- Ongoing maintenance—updates, encrypted backups, and access reviews—preserves privacy guarantees over time
A properly hardened FrankBoard deployment on a VPS offers privacy guarantees no cloud SaaS can match: your data never leaves infrastructure you control, subject only to your own security practices rather than a vendor's business model or legal jurisdiction.