Files
ServerKit/docs/LOCAL_DEVELOPMENT.md
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

7.8 KiB

Local Development Setup for Windows

This guide explains how to run ServerKit locally on Windows for development and testing.

Production Server Specs (Reference)

ServerKit is tested on Digital Ocean with these specs:

Spec Value
OS Ubuntu 24.04.3 LTS
CPU 2 cores (AMD)
RAM 4 GB
Disk 25 GB
Plan Basic Droplet (~$12/mo)

For local development, similar or lower specs work fine.

Prerequisites

  • Windows 10/11 with WSL2 enabled
  • Docker Desktop for Windows (with WSL2 backend)
  • Git for Windows

This mimics the production setup where backend runs on host and frontend in Docker.

Step 1: Install WSL2 + Ubuntu

# Run in PowerShell as Administrator
wsl --install -d Ubuntu-24.04

Restart your computer, then open Ubuntu from Start menu to complete setup.

Step 2: Install Dependencies in WSL

# Update packages
sudo apt update && sudo apt upgrade -y

# Install Python
sudo apt install -y python3 python3-pip python3-venv python3-dev

# Install build tools
sudo apt install -y build-essential libffi-dev libssl-dev

# Install iproute2 (for ss command)
sudo apt install -y iproute2

# Install Docker (inside WSL)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# Log out and back in for docker group to take effect

Step 3: Clone and Setup

# Clone the repo (or navigate to your mounted Windows folder)
cd ~
git clone https://github.com/jhd3197/ServerKit.git
cd ServerKit

# Create virtual environment
python3 -m venv venv
source venv/bin/activate

# Install dependencies
pip install -r backend/requirements.txt
pip install gunicorn gevent gevent-websocket

# Create .env file
cat > .env << 'EOF'
SECRET_KEY=dev-secret-key-change-in-production
JWT_SECRET_KEY=dev-jwt-key-change-in-production
DATABASE_URL=sqlite:///backend/instance/serverkit.db
CORS_ORIGINS=http://localhost:41921,http://127.0.0.1:41921,http://localhost:47927,http://127.0.0.1:47927
FLASK_ENV=development
PORT=47927
EOF

# Create instance directory
mkdir -p backend/instance

Step 4: Run Backend (Terminal 1)

cd ~/ServerKit
source venv/bin/activate
cd backend
export $(cat ../.env | xargs)
python run.py

Backend will be available at: http://localhost:47927

Step 5: Run Frontend (Terminal 2)

cd ~/ServerKit/frontend
npm install
npm run dev -- --host 127.0.0.1 --port 41921 --strictPort

Frontend will be available at: http://localhost:41921


Option 2: Docker Compose (Simpler, but no host system access)

Use this for quick UI testing. Note: System management features (PHP install, firewall, etc.) won't work.

The repository's docker-compose.yml already does this — it builds the all-in-one image from Dockerfile (gunicorn serving the API, the SPA and websockets together) and publishes it on port 5000.

cp .env.example .env    # set SECRET_KEY / JWT_SECRET_KEY
docker compose up -d --build

Then open http://localhost:5000. Override the published port with SERVERKIT_HTTP_PORT in .env if 5000 is taken.

docker compose logs -f          # follow the panel log
docker compose down             # stop, keep the database volume
docker compose down -v          # stop and destroy the database

There is no hot reload in this mode — the SPA is a production build baked into the image, so a frontend change needs docker compose up -d --build. For an edit-refresh loop, use Option 1 (Vite HMR + Flask reloader).


Option 3: VS Code Dev Container

Add this to .devcontainer/devcontainer.json:

{
  "name": "ServerKit Dev",
  "dockerComposeFile": "../docker-compose.yml",
  "service": "serverkit",
  "workspaceFolder": "/app",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "dbaeumer.vscode-eslint"
      ]
    }
  },
  "forwardPorts": [5000],
  "postCreateCommand": "pip install -r backend/requirements.txt"
}

Quick Reference

Component Production (DO Server) Local Dev (WSL)
Backend systemd service python run.py
Frontend Docker + nginx npm run dev
Database SQLite in /opt/serverkit SQLite in ./backend/instance
System Access Full (sudo, systemctl) Full (in WSL)

Testing System Features

To test PHP installation, firewall, etc. locally:

  1. Use Option 1 (WSL) - this gives you a real Linux environment
  2. Install the same packages as production:
    sudo apt install -y firewalld vsftpd
    
  3. The backend will have real system access

Environment Variables

Variable Description Default
SECRET_KEY Flask secret key Required
JWT_SECRET_KEY JWT signing key Required
DATABASE_URL SQLite path sqlite:///serverkit.db
CORS_ORIGINS Allowed origins http://localhost
FLASK_ENV development/production production

Troubleshooting

Port already in use

# Find what's using the port
sudo lsof -i :47927
# Kill it
sudo kill -9 <PID>

Permission denied for Docker

sudo usermod -aG docker $USER
# Log out and back in

Database locked

# Remove the lock file
rm backend/instance/serverkit.db-journal

Exposing Your Local Control Plane (ngrok)

When developing locally on Windows/WSL you may want a remote ServerKit agent (or a Docker host on another machine) to connect back to your machine. The dev.sh and dev.ps1 scripts include a tunnel mode that starts the backend, frontend, and an ngrok tunnel pointed at the active backend port printed by the launcher.

Prerequisites

  1. Install ngrok and authenticate once:

    # macOS / Linux / WSL
    brew install ngrok/ngrok/ngrok    # or: snap install ngrok
    ngrok config add-authtoken <YOUR_TOKEN>
    
    # Windows
    choco install ngrok               # or: scoop install ngrok
    ngrok config add-authtoken <YOUR_TOKEN>
    
  2. (Optional) Reserve a domain in the ngrok dashboard so the URL stays stable across restarts.

Start with a tunnel

# Linux / macOS / WSL
./dev.sh tunnel
# Windows
.\dev.ps1 tunnel

The public ngrok URL is printed in green once the tunnel is up. Use it as the --server argument when registering an agent:

serverkit-agent register \
  --token "sk_reg_xxx" \
  --server "https://<your-tunnel>.ngrok-free.app"

Optional environment variables

Variable Purpose
NGROK_DOMAIN Use a reserved ngrok domain (e.g. my-app.ngrok-free.app).
NGROK_AUTHTOKEN Override the authtoken (otherwise the one stored by ngrok config is used).
CORS_ORIGINS Override CORS allow-list. The script defaults include https://*.ngrok-free.app, https://*.ngrok.app, https://*.ngrok.io.

Quick Start with WSL2 (Helper Script)

If you're on Windows and using WSL2 Ubuntu/Debian, you can skip the manual steps above and use the bundled setup helper:

# from inside WSL, in the cloned repo
bash scripts/setup-wsl.sh           # full setup
bash scripts/setup-wsl.sh --check   # dry-run / diagnostics only

The script will:

  • Detect WSL and warn if the repo lives under /mnt/c (slow file I/O for hot-reload).
  • Verify Ubuntu/Debian and install missing apt packages (python3, python3-venv, python3-pip, nodejs, npm, git, curl).
  • Create backend/venv and install Python requirements.
  • Run npm install in frontend/.
  • Copy backend/.env.example to backend/.env if missing.
  • Print WSL-specific tips (localhost forwarding, polling reloader, ngrok).

Once it finishes:

./dev.sh           # start backend + frontend
./dev.sh tunnel    # also expose backend via ngrok (see section above)