Files
Juan Denis 45bc3c70bd Guard first admin with a setup code, fix Git deploys
A fresh panel no longer lets whoever reaches it first become admin:
registering the first account needs a one-time code the installer
prints (`serverkit setup-code` repeats it). SERVERKIT_ADMIN_EMAIL and
SERVERKIT_ADMIN_PASSWORD create the admin unattended instead.

Git apps now deploy what users expect: a repository with a compose
file deploys every service it declares, "Deploy latest" builds the
branch's newest commit, and push webhooks rebuild the containers.
Also fixes branch switching on single-branch clones, uploads of a
lone file, compose v1 port preflight, and CLI commands that ignored
their flags.
2026-09-24 00:50:02 -04:00

10 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

# Show the setup code the first admin registration needs
serverkit setup-code

# Create admin user
serverkit create-admin
# Prompts for: email, username, password
# (or: serverkit create-admin --email a@b.com --username admin --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