# ============================================ # ServerKit Configuration # ============================================ # Copy this file to .env and customize for your environment # Generate secure keys with: python -c "import secrets; print(secrets.token_hex(32))" # ============================================ # ---------------------------------------- # REQUIRED: Security Keys # ---------------------------------------- # Generate unique keys for production! SECRET_KEY=your-secret-key-here JWT_SECRET_KEY=your-jwt-secret-key-here # Fernet key for encrypting secrets at rest. # Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" SERVERKIT_ENCRYPTION_KEY=your-fernet-key-here # ---------------------------------------- # Database Configuration # ---------------------------------------- # SQLite (default - good for small deployments). # NOTE the path is relative to the backend working directory. # DATABASE_URL=sqlite:///serverkit.db # PostgreSQL (recommended for production): # DATABASE_URL=postgresql://user:password@localhost:5432/serverkit # MySQL: # DATABASE_URL=mysql://user:password@localhost:3306/serverkit # ── Under Docker, use SERVERKIT_DATABASE_URL instead ── # docker-compose.yml pins DATABASE_URL to /app/instance/serverkit.db, the path # backed by the persistent volume, and ignores the DATABASE_URL above. That is # on purpose: a *relative* sqlite path resolves inside the container and is # destroyed by the next `compose down`. To point Docker at another database, # set this instead — it wins: # SERVERKIT_DATABASE_URL=postgresql://user:password@db:5432/serverkit # ---------------------------------------- # Network Configuration # ---------------------------------------- # CORS Origins - your domain(s), comma-separated CORS_ORIGINS=http://localhost,https://your-domain.com # Public URL that agents should dial back to. Set this whenever the panel # sits behind a reverse proxy / tunnel / custom domain — the connection # string the panel issues to agents will embed this URL instead of the # request's Host header (which would otherwise be the internal IP/port). # Leave commented out for plain localhost development. # SERVERKIT_PUBLIC_URL=https://panel.your-domain.com # Port. Only the Flask *development* server reads this (`python run.py`). # Under Docker the container always listens on 5000 and you publish it with # SERVERKIT_HTTP_PORT (see docker-compose.yml); under gunicorn the port comes # from SERVERKIT_BACKEND_PORT. # PORT=5000 # ---------------------------------------- # Trusted reverse proxy (client IP) # ---------------------------------------- # Derive the real client IP from X-Forwarded-For (via Werkzeug ProxyFix) for # rate limiting, login lockout and audit logs, instead of logging every request # as the proxy's address. # # Turn it on ONLY when a proxy you control is the sole route in. The header is # whatever the caller typed, so if anything can open a connection to the backend # port directly, this hands attackers the ability to forge the IP that those # very throttles key on. # # Off by default HERE because this file is the Docker/manual path, and # docker-compose.yml publishes port 5000 on all interfaces. Set it to true once # you put a proxy in front AND stop direct access to 5000 — bind it to loopback # (see docs/INSTALLATION.md → "Running behind your own reverse proxy") or # firewall it. # # Host installs do not use this file: install.sh writes its own .env and enables # this automatically, because there gunicorn binds loopback behind our nginx. # # TRUSTED_PROXY_HOPS = proxies in front of Flask. Your proxy alone = 1; your # proxy plus ServerKit's nginx = 2; add Cloudflare on top and it goes up again. TRUST_PROXY_HEADERS=false TRUSTED_PROXY_HOPS=1 # ---------------------------------------- # Login brute-force throttle (per client IP) # ---------------------------------------- # On top of the per-user account lockout, block a client IP after too many # failed logins (defends against password-spraying across many usernames). # Defaults shown; override only if you need to tune them. # AUTH_IP_MAX_ATTEMPTS=10 # AUTH_IP_WINDOW_MINUTES=15 # AUTH_IP_BLOCK_MINUTES=15 # ---------------------------------------- # Rate-limit storage backend # ---------------------------------------- # Default is in-memory, which matches the deliberate single-worker gunicorn # design (see docs/ARCHITECTURE.md). Point at Redis to share limit state # across processes and silence flask-limiter's in-memory production warning. # RATELIMIT_STORAGE_URI=redis://localhost:6379/0 # ---------------------------------------- # Environment # ---------------------------------------- # Options: development, production FLASK_ENV=production # ---------------------------------------- # GitHub Repository (for agent downloads) # ---------------------------------------- # This is used to fetch agent releases for the downloads page SERVERKIT_GITHUB_REPO=jhd3197/ServerKit # ---------------------------------------- # Theme Registry # ---------------------------------------- # Curated theme index for the Browse gallery. Unset = the public registry # (https://serverkit.ai/themes/index.json); set-but-empty = disabled (bundled # themes only). # SERVERKIT_THEMES_REGISTRY_URL=https://serverkit.ai/themes/index.json # Seconds to cache the registry index before re-fetching (default 3600). # SERVERKIT_THEMES_REGISTRY_TTL=3600 # ---------------------------------------- # Optional: Notification Webhooks # ---------------------------------------- # These can also be configured via the web UI at Settings > Notifications # Discord Webhook URL # DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... # Slack Webhook URL # SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... # Telegram Bot # TELEGRAM_BOT_TOKEN=your-bot-token # TELEGRAM_CHAT_ID=your-chat-id # ---------------------------------------- # Security opt-ins & special modes # ---------------------------------------- # Allow installed extensions to pip-install the Python requirements they ship. # Off by default: pip runs with the backend's privileges. # SERVERKIT_ALLOW_PLUGIN_PIP=false # Allow extension downloads from non-HTTPS / private-network URLs. # SERVERKIT_ALLOW_PRIVATE_DOWNLOADS=false # Read-only demo panel (mutating API calls are refused). # SERVERKIT_DEMO_MODE=false # Stream simulated deploys through the real deploy pipeline (no Docker needed). # SERVERKIT_DEMO_DEPLOYS=false # Mark this panel as a staging install (reported by /health). # SERVERKIT_STAGING=false # Add request duration, SQL duration and statement count to Server-Timing. # Temporary local/staging diagnosis; disabled by default. No SQL text is emitted. # SERVERKIT_PROFILE_REQUESTS=false