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

Tier
vCPU
RAM
Disk
Suitable For

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

Software
Minimum Version
How to Check

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 standalone docker-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.com

  • codev.yourcompany.com

  • devops.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:

Field
Value

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

Port
Protocol
Purpose
Required When

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_IPS in your .env file (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:

Ingress
Public side
Private side

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 sfp

  • The SSH user must have permission to run docker commands (e.g., member of the docker group, or root)

[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

After completing this section, you should have:

Value
Example
Source

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:

Header
Purpose

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 010).

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:

What
Where
Guide

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.10

  • Multiple IPs: ALLOWED_IPS=203.0.113.10,198.51.100.5

  • CIDR ranges: ALLOWED_IPS=10.0.0.0/8,172.16.0.0/12

After changing ALLOWED_IPS, restart Caddy with docker compose restart caddy for 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:

Secret
When
Description

DOCKER_REGISTRY_TOKEN

Always

Pulls the server image — see Setting up sfp server → [1.1]

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 secrets block of server.json and 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?