Files
ServerKit/docs/DEPLOYMENT.md
T
Juan DenisandClaude Opus 5 2624816680 docs: how to run the panel behind your own reverse proxy
Issue #96: an operator fronting the panel with Caddy and a smallstep private CA
hit a redirect loop, and there was no documented path to follow. The
architecture already tolerated this deployment -- the fix was a config file we
already ship -- but nothing said so anywhere.

Adds an INSTALLATION.md section covering both topologies (external proxy ->
ServerKit nginx, full features, HOPS=2; external proxy -> the app directly,
panel-only, HOPS=1), why the loop happens, how to count TRUSTED_PROXY_HOPS,
Caddy (with acme_ca for a private CA), Traefik, Nginx Proxy Manager and plain
nginx snippets, what a private CA does and does not require (the panel never
sees it; a remote agent's host must trust the root), and a symptom-to-cause
table for the loop, stale HSTS, the rejected websocket and wrong client IPs.

Also corrects the Docker documentation, which had drifted badly:

- README (en/es/pt/zh-CN), INSTALLATION and DEPLOYMENT all told people to run
  `docker compose up -d` and reach the panel at http://localhost. It starts the
  all-in-one container on :5000, and a containerised panel cannot manage its own
  host -- now stated up front rather than discovered.
- DEPLOYMENT is a host-install guide that told operators to run
  `docker compose build && up` to fix a panel, which would build a second panel
  beside theirs, and carried a certbot cron copying certs and restarting a
  container that does not exist on a host install.
- CLAUDE.md, AGENTS.md, CONTRIBUTING.md and LOCAL_DEVELOPMENT.md (x3) pointed at
  a docker-compose.dev.yml that has never existed; the snippet offering to
  create it referenced a frontend/Dockerfile.dev that does not exist either.

Every anchor introduced here was checked to resolve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 23:07:27 -04:00

9.9 KiB

ServerKit Deployment Guide

Complete guide for deploying ServerKit on Ubuntu servers.

Deployment model (important)

ServerKit's backend manages the host it runs on — it creates app directories under /var/serverkit/apps, drives the host Docker daemon (one container per managed app), reloads nginx, and runs systemctl/firewall/PHP-FPM. A backend running inside a container cannot do any of that. So the canonical deployment installed by ./serverkit install (install.sh) runs fully off Docker for the panel itself:

  • the backend on the host via systemd (serverkit.service), and
  • the frontend as static files served directly by host nginx from frontend/dist (built by install.sh) — no container. Host nginx serves the SPA at location / and proxies /api + /socket.io to the host backend on :5000.

Docker is therefore used only for the workloads you host (one container per managed app). This is deliberate: a ServerKit update never touches those containers, and host nginx — which reverse-proxies them — is only ever reloaded, never stopped, so updating the panel causes zero downtime for hosted apps.

Do not add the backend into Docker — a containerized backend fails app/WordPress creation with Permission denied: /var/serverkit/apps/... and has no access to the host Docker daemon. A containerized frontend is still available as an opt-in escape hatch (docker compose --profile legacy-frontend up -d, plus repointing nginx location / at 127.0.0.1:3847) but is not used by the default install. The single-container image in ./Dockerfile exists only for throwaway demos and, as its header states, cannot manage the host.

Quick Install (Ubuntu)

# One-line install
curl -fsSL https://serverkit.ai/install.sh | bash

# Or clone and install manually
git clone https://github.com/jhd3197/serverkit.git
cd serverkit
chmod +x serverkit
./serverkit install

Prerequisites

  • Ubuntu 20.04+ (or Debian 11+)
  • Docker 20.10+ and Docker Compose 2.0+
  • At least 1GB RAM
  • Domain name (for SSL/HTTPS)

CLI Commands

ServerKit includes a management CLI for common administrative tasks.

Service Management

# Start/Stop/Restart
serverkit start
serverkit stop
serverkit restart

# View status
serverkit status

# View logs
serverkit logs              # All services
serverkit logs backend      # Backend only
serverkit logs frontend     # Frontend only

# Update to latest version
serverkit update

# Uninstall
serverkit uninstall

User Management

# Create admin user
serverkit create-admin
# Prompts for: email, username, password

# Reset a user's password
serverkit reset-password
# Prompts for: email, new password

# Unlock a locked account (after failed login attempts)
serverkit unlock-user
# Prompts for: email

# List all users
serverkit list-users

# Promote user to admin
serverkit make-admin
# Prompts for: email

# Deactivate/Activate user
serverkit deactivate-user
serverkit activate-user

Database Management

# Initialize database
serverkit init-db

# Apply database migrations (after updates)
serverkit migrate-db

# Backup database
serverkit backup-db
# Creates: backup/serverkit-YYYYMMDD-HHMMSS.db

# Restore from backup
serverkit restore-db backup/serverkit-20240115-120000.db

App & Cleanup Commands

# List all installed applications (from database)
serverkit list-apps

# Also show all Docker containers (including infrastructure)
serverkit list-apps --all

# Delete all apps, containers, folders, and orphaned Docker resources
serverkit cleanup-apps

# Also delete Docker volumes
serverkit cleanup-apps --delete-volumes

# Keep database records (only delete containers/folders)
serverkit cleanup-apps --keep-db

# Complete factory reset (delete everything and start fresh)
# Preserves only serverkit-frontend infrastructure
serverkit factory-reset

Note: Cleanup commands will:

  • Stop and remove all app containers
  • Remove orphaned Docker containers (not tracked in database)
  • Prune unused Docker networks
  • Delete all folders in /var/serverkit/apps/
  • Never touch ServerKit infrastructure (serverkit-frontend, serverkit-network)

Utility Commands

# Generate secure keys for .env
serverkit generate-keys

# Edit configuration
serverkit config

# Open shell in backend container
serverkit shell

# Show help
serverkit help

Manual Installation

1. Install Docker

# Install Docker
curl -fsSL https://get.docker.com | sh

# Add user to docker group
sudo usermod -aG docker $USER

# Log out and back in, then verify
docker --version

2. Clone Repository

git clone https://github.com/jhd3197/serverkit.git /opt/serverkit
cd /opt/serverkit

3. Configure Environment

# Generate secure keys
serverkit generate-keys

# Create .env file
cat > .env << 'EOF'
SECRET_KEY=<paste-generated-key>
JWT_SECRET_KEY=<paste-generated-key>
DATABASE_URL=sqlite:///serverkit.db
CORS_ORIGINS=https://yourdomain.com
PORT=80
SSL_PORT=443
FLASK_ENV=production
EOF

4. SSL Certificate Setup

# Install certbot
sudo apt install certbot

# Get certificate (stop any service on port 80 first)
sudo certbot certonly --standalone -d yourdomain.com

# Copy certificates
sudo cp /etc/letsencrypt/live/yourdomain.com/fullchain.pem nginx/ssl/
sudo cp /etc/letsencrypt/live/yourdomain.com/privkey.pem nginx/ssl/
sudo chown -R $USER:$USER nginx/ssl/

Option B: Self-Signed (Development Only)

mkdir -p nginx/ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout nginx/ssl/privkey.pem \
  -out nginx/ssl/fullchain.pem \
  -subj "/CN=localhost"

5. Build and Start

Both panel tiers run on the host: the backend under systemd (serverkit.service) and the frontend as static files served by host nginx from frontend/dist. ./serverkit install builds the bundle and wires nginx for you. See the Deployment model note above.

# Build the SPA bundle host nginx serves (install.sh does this automatically)
cd frontend && npm ci && npm run build && cd ..

# Start the host backend + nginx
sudo systemctl start serverkit
sudo systemctl reload nginx

# Verify
serverkit status

6. Create Admin User

serverkit create-admin

Systemd Service

Run ServerKit as a system service:

# Copy service file
sudo cp deploy/serverkit.service /etc/systemd/system/

# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable serverkit
sudo systemctl start serverkit

# Check status
sudo systemctl status serverkit

Configuration Reference

Environment Variables

Variable Description Default
SECRET_KEY Flask secret key Required
JWT_SECRET_KEY JWT signing key Required
DATABASE_URL Database connection string sqlite:///serverkit.db
CORS_ORIGINS Allowed CORS origins http://localhost
PORT HTTP port 80
SSL_PORT HTTPS port 443
FLASK_ENV Environment mode production

Host data locations

The backend runs on the host, so its data lives on the host filesystem (not in Docker volumes):

Path Purpose
/opt/serverkit/backend/instance/serverkit.db SQLite database (default)
/var/serverkit/apps/ Managed app directories (one per app)
/etc/serverkit/ Config (templates, ssl-mode, install state)
/var/backups/serverkit/ Backups

Common Tasks

Auto-renew SSL Certificate

Certbot installs its own renewal timer, and host nginx reads the certificates straight out of /etc/letsencrypt/live/, so there is nothing to copy and no container to restart. Verify it is armed:

# The renewal timer certbot installed
systemctl list-timers | grep certbot

# Dry-run a renewal without touching the live certificate
sudo certbot renew --dry-run

Only if the timer is missing, add a cron entry that reloads host nginx:

sudo crontab -e
# Renew at 2:30 AM daily, reload nginx only when something actually renewed
30 2 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"

Change Domain

  1. Update .env file: CORS_ORIGINS=https://newdomain.com
  2. Get new SSL certificate
  3. Restart: serverkit restart

Scale for Production

For high-traffic deployments, consider:

# Use PostgreSQL instead of SQLite
DATABASE_URL=postgresql://user:pass@localhost:5432/serverkit

# Add Redis for session storage
# Edit docker-compose.yml to add redis service

Troubleshooting

Panel won't start

This guide covers the host install, where the panel is a systemd service and not a container — do not reach for docker compose here, it would build a second, separate all-in-one panel beside this one.

# Check logs
serverkit logs backend

# Restart the service
serverkit restart
serverkit status

# Deeper diagnosis
serverkit doctor

Permission denied errors

# Fix Docker socket permissions
sudo chmod 666 /var/run/docker.sock

# Or add user to docker group
sudo usermod -aG docker $USER
# Then log out and back in

Port already in use

# Find what's using port 80 (host nginx) or 5000 (the panel backend)
sudo lsof -i :80
sudo lsof -i :5000

Reset everything

# Delete all managed apps, containers, folders and orphaned Docker resources
serverkit factory-reset

# Bring the panel back up
serverkit start
serverkit init-db
serverkit create-admin

Locked out / Forgot password

# Reset password via CLI
serverkit reset-password
# Enter email and new password

# Or unlock account if locked
serverkit unlock-user

Security Checklist

  • Generated unique SECRET_KEY and JWT_SECRET_KEY
  • Using HTTPS with valid SSL certificate
  • Firewall configured (allow only ports 80, 443, 22)
  • Regular backups configured
  • Admin password is strong
  • Updated to latest version

Support