Prerequisites
This document outlines everything you need to prepare before running sfp server init. Completing these steps in advance will ensure a smooth installation.
Download the sfp CLI from source.flxbl.io/flxbl/sfp-pro/releases. Install it on whichever machine will run the init command — your local workstation (remote via SSH) or the server itself (local mode).
[1] Server Requirements
[1.a] Hardware
Minimum
4
16 GB
100 GB SSD
Small team (< 10 developers)
Recommended
8
32 GB
250 GB SSD
Medium team (10–50 developers)
Production
16
64 GB
500 GB SSD
Large team (50+), heavy parallel workflows
Disk should be SSD. Workflow orchestration and database queries are I/O intensive.
[1.b] Operating System
Linux x86_64 (Ubuntu 22.04+ or Debian 12+ recommended)
ARM64 (e.g., AWS Graviton) is not supported at this time
[1.c] Software
Docker Engine (default) or Podman
Docker 24.x+ / Podman 4.x+
docker --version or podman --version
Docker Compose v2
v2.x (plugin or standalone)
docker compose version or podman compose version
SELinux graphroot label (Podman rootless on RHEL/Fedora only)
data_home_t or container_var_lib_t
ls -Zd $(podman info --format '{{.Store.GraphRoot}}') — must not show user_home_t. See Podman Support → [1.c]
sfp CLI
Latest
sfp --version
Docker Compose must be v2 (
docker compose), not the legacy standalonedocker-compose. See Setting up Docker on the server for Docker installation, or Podman Support for Podman setup. Podman users:podman-compose(Python) is not supported — Docker Compose v2 is required as the Compose backend.
[2] Network & DNS
[2.a] Step 1: Choose your domain
Pick a subdomain for your SFP Server. Examples:
flxbl.yourcompany.comcodev.yourcompany.comdevops.internal.yourcompany.com(private/internal domains work too)
[2.b] Step 2: Create a DNS record
In your DNS provider (Cloudflare, Route 53, GoDaddy, etc.), create an A record:
Type
A
Name
sfp (or your chosen subdomain)
Value
Your server's IP address
Proxy
Off / DNS-only (if using Cloudflare, set to grey cloud)
TTL
Auto or 300
[2.c] Step 3: Verify DNS resolves
If using a private/internal domain, verify from a machine on the same network.
[2.d] Step 4: Choose your TLS mode
Pick a mode and gather the inputs listed below. Full configuration steps live in Setting up sfp server → [2] Configure TLS.
Mode
--tls-mode value
Best for
You need ready before init
Bring Your Own Cert
cloudflare (default)
Enterprise / private networks, corporate CA, Cloudflare Origin CA
ORIGIN_CERT + ORIGIN_KEY (base64-encoded PEM)
Let's Encrypt
letsencrypt
Public-facing servers, quick evaluation
Ports 80 + 443 open, public DNS resolving
Custom / On-demand
custom
On-demand TLS via Caddy
Nothing — Caddy manages certs
Behind Load Balancer
none
Existing infra (ALB, F5, NGINX, tunnel, API Gateway) terminates TLS
Public HTTPS ingress forwards HTTP to Caddy and sets or preserves X-Forwarded-Proto: https
[2.e] Firewall / Security Group
443
TCP
HTTPS — main application entry point
Always (except Option C)
80
TCP
Let's Encrypt ACME challenge
Only for Option B (--tls-mode letsencrypt)
8080
TCP
Hatchet Dashboard (workflow monitoring)
Restrict to admin IPs
4873
TCP
Verdaccio (private npm registry)
Restrict to admin/CI IPs
3100
TCP
Supabase Studio (database management)
Restrict to admin IPs
Ports 8080, 4873, and 3100 are IP-restricted by Caddy. To configure the allowlist, set
ALLOWED_IPSin your.envfile (comma-separated IPs or CIDR ranges). These ports do not need to be open to all users.
For GitHub.com, Azure DevOps, and other SaaS webhook providers, the HTTPS entry point must be reachable from the public internet. Private-only DNS names, bastion-only hosts, VPN-only hosts, and SSH ProxyJump paths are valid for operators, but not for inbound webhooks.
If the server itself is private, front it with public HTTPS ingress:
AWS ALB / customer load balancer
Public DNS + valid TLS cert on port 443
Target group forwards HTTP to Caddy; backend security group allows only LB-to-server traffic
Reverse proxy / tunnel
Public HTTPS URL
Named tunnel or supervised proxy forwards to the private server; quick tunnels are for smoke/dev only
API Gateway
Public API Gateway HTTPS endpoint
HTTP API private integration uses VPC Link to an internal ALB/NLB, then forwards to Caddy with the correct backend path
The backend host stays private. For webhook-only ingress, expose only POST /sfp/api/repository/webhook on the public ingress and reject other public paths unless you intentionally publish the full sfp UI/API.
For GitHub.com webhooks, do not depend on source-IP allowlists as the primary control. IP ranges change and source IP does not prove authenticity. Expose only the public HTTPS ingress, keep the backend private to that ingress, and let sfp-server validate the provider signature.
For webhook ingress, verify the exact payload URL from the webhook provider, not only the root domain. The ingress must allow HTTPS POST, forward the raw request body, preserve signature/event headers, and return a 2xx response quickly.
See Webhook ingress for private servers, Setting up sfp server → [2.c] Behind a Load Balancer, and Troubleshooting → Webhooks if webhook deliveries time out.
[2.f] SSH Access
The sfp server init command can be run remotely from your local machine via SSH, or locally on the server itself. If running remotely:
The server must be accessible via SSH from the machine running
sfpThe SSH user must have permission to run
dockercommands (e.g., member of thedockergroup, orroot)
[2.g] Outbound Access (Server)
The server requires outbound internet access for:
Docker image pulls (GitHub Container Registry, Docker Hub) — during init and updates
GitHub API — for OAuth authentication and repository operations at runtime
If using Option B (--tls-mode letsencrypt), the server also needs:
Let's Encrypt ACME (ports 80/443 outbound)
The Supabase CLI is downloaded once during init as part of the database migration container. This happens inside Docker and requires the server to reach
github.com.
[2.h] What you'll provide to sfp server init
sfp server initAfter completing this section, you should have:
Domain
sfp.yourcompany.com
[2.a]
TLS mode
cloudflare, letsencrypt, custom, or none
[2.d]
ORIGIN_CERT (cloudflare mode only)
Base64-encoded PEM certificate
[2.d]
ORIGIN_KEY (cloudflare mode only)
Base64-encoded PEM private key
[2.d]
[2.i] Reverse proxy headers
If sfp-server sits behind one or more reverse proxies (Cloudflare, a customer load balancer, NGINX, etc.), every proxy in the chain must preserve or set these headers:
X-Forwarded-For
Client IP — used for IP attribution, rate limiting, monitoring allowlists, and audit logs. Controlled by trustProxyHops (Express trust proxy, default 2, range 0–10).
X-Forwarded-Proto
Original protocol (https) — used for request-origin detection and redirect URLs. Read separately, not gated by trustProxyHops.
X-Forwarded-Host
Original hostname — used for auth callbacks and link generation. Read separately, not gated by trustProxyHops.
See Reference → Properties → trustProxyHops and Troubleshooting → Proxy / Client IP if client IPs appear wrong after deployment.
[3] Git Provider & Login — configured after the server is up
You do not set up GitHub, Azure DevOps, or login auth here. None of it is needed for sfp server init. Once the server is running, you connect your git provider and configure authentication from the codev UI:
Git provider (GitHub App / Azure DevOps service principal) + repository integration
Settings → Integrations (and the onboarding wizard)
Login auth (GitHub OAuth / Azure OAuth / SAML SSO)
.env + restart, or SAML connection
You can prepare credentials in advance (a GitHub App or the flxbl-cloud install; an Azure service principal), but you enter them post-boot — see the guides above.
[4] Admin IP Addresses
Collect the public (or private, if on a corporate network) IP addresses of anyone who needs access to administrative dashboards:
Hatchet Dashboard (workflow monitoring) — port 8080
Supabase Studio (database management) — port 3100
Verdaccio (npm registry browser) — port 4873
Set these as the ALLOWED_IPS value in your .env file after init:
Single IPs:
ALLOWED_IPS=203.0.113.10Multiple IPs:
ALLOWED_IPS=203.0.113.10,198.51.100.5CIDR ranges:
ALLOWED_IPS=10.0.0.0/8,172.16.0.0/12
After changing
ALLOWED_IPS, restart Caddy withdocker compose restart caddyfor the change to take effect.
[5] Secrets / Configuration
sfp server init reads its secrets from --config-file (recommended), environment variables, or a --secrets-provider (Infisical, AWS Secrets Manager). All database credentials (Postgres password, JWT secret, Supabase API keys) are auto-generated — you do not create them.
Only two things are genuine init-time secrets:
ORIGIN_CERT + ORIGIN_KEY
--tls-mode cloudflare
Base64-encoded PEM cert + key (Section 2, Option A)
Git provider credentials, login OAuth, and the GitHub Packages token are not init secrets — you configure them after the server is up (see Section 3). Don't put them in
server.json.
Place init secrets in the
secretsblock ofserver.jsonand pass it via--config-file ./server.json— the canonical path for repeatable deployments. See Setting up sfp server → [1] for the file shape.
[6] Pre-Installation Checklist
Use this checklist to confirm readiness before running sfp server init:
Next Steps
Once all prerequisites are in place, proceed to Setting up sfp server for installation, configuration, and first-run instructions.
Last updated
Was this helpful?