SFP Server Self-Hosted Deployment
Automated deployment and management of self-hosted SFP Pro server instances using GitHub Actions.
Overview
This repository provides GitHub Actions workflows to:
- Initialize a new SFP server on your infrastructure
- Update an existing server to the latest version
- Check for new versions on a weekly schedule
The workflows connect to your server via SSH, pull the SFP server Docker image, and run the CLI from inside it to manage the server lifecycle.
Prerequisites
Before using this repository, ensure you have:
- Linux server -- x86_64, 8+ vCPU, 32+ GB RAM, 250+ GB SSD
- Docker Engine 24+ and Docker Compose v2 installed on the server
- Domain name (FQDN) resolving to the server
- SSH access to the server from GitHub Actions runners
- Docker registry token for pulling SFP server images (the CLI runs from inside the image)
- Port 443 (HTTPS) or port 80 (HTTP) open depending on TLS mode
For detailed requirements, see the Self-Hosting Prerequisites guide.
Quick Setup
1. Fork or Clone This Repository
Fork this repository to your GitHub organization, or clone and push to a new private repository.
2. Configure GitHub Secrets
Go to Settings > Secrets and variables > Actions > Secrets and add:
| Secret | Description |
|---|---|
SSH_PRIVATE_KEY |
SSH private key for connecting to the server |
DOCKER_REGISTRY_TOKEN |
Token for authenticating with your Docker registry |
ORIGIN_CERT |
TLS certificate in base64 PEM (only if using custom TLS) |
ORIGIN_KEY |
TLS private key in base64 PEM (only if using custom TLS) |
3. Configure GitHub Variables
Go to Settings > Secrets and variables > Actions > Variables and add:
| Variable | Required | Default | Description |
|---|---|---|---|
SSH_HOST |
Yes | -- | Server hostname or IP address |
TENANT_NAME |
Yes | -- | Tenant identifier (lowercase, alphanumeric, hyphens) |
DOMAIN |
Yes | -- | FQDN for the server (e.g., sfp.yourcompany.com) |
DOCKER_REGISTRY |
Yes | -- | Docker registry hostname (e.g., source.flxbl.io) |
SSH_USER |
No | root |
SSH username |
IMAGE_FQDN |
Yes | -- | Full Docker image path (e.g., ghcr.io/flxbl-io/sfp-server-rc) |
IMAGE_TAG |
No | latest |
Docker image tag (e.g., latest, v3-latest, 3.28.0) |
WORKERS |
No | 1 |
Number of background workers (1-10) |
BASE_DIR |
No | ./sfp-server |
Base directory on the server |
4. Run Initialization
- Go to Actions > Initialize SFP Server
- Click Run workflow
- Select the environment and TLS mode (see below)
- Click Run workflow
TLS Modes
| Mode | When to Use | Requirements |
|---|---|---|
letsencrypt |
Server is internet-accessible, you want automatic TLS | Domain DNS points to server, port 80 + 443 open |
custom |
You have your own TLS certificates (internal CA, commercial CA) | ORIGIN_CERT and ORIGIN_KEY secrets set (base64 PEM) |
none |
You run your own reverse proxy (nginx, HAProxy, ALB) in front of SFP | External proxy handles TLS, port 80 open for Caddy HTTP |
The init process will:
- Pull the SFP server Docker image (the CLI runs from inside it)
- Connect to your server via SSH
- Create the directory structure and configuration
- Auto-generate database credentials
- Pull Docker images from your registry
- Create the default admin user
5. Retrieve Admin Credentials
Admin credentials are not printed in workflow logs for security. They are stored on the server in a protected file. Retrieve them via SSH:
ssh <user>@<host> "cat <base-dir>/tenants/<tenant>/credentials.json"
The file contains:
adminEmail-- admin login emailadminPassword-- admin login passwordstudioUser/studioPassword-- Supabase Studio dashboard credentials
6. Verify
Open https://your-domain (or http://your-domain if using tls-mode=none) in a browser to verify the server is accessible.
Post-Init: Integration Setup
After initialization, configure integrations by SSH-ing to the server and editing the .env file, or via the integration API:
GitHub OAuth (for user login)
- Create a GitHub OAuth App (instructions)
- Homepage URL:
https://your-domain - Callback URL:
https://your-domain/auth/v1/callback
- Homepage URL:
- SSH to the server and add to
.env:GITHUB_OAUTH_ENABLED=true GITHUB_OAUTH_CLIENT_ID=your-client-id GITHUB_OAUTH_CLIENT_SECRET=your-client-secret - Restart services:
docker compose restart supabase-auth
GitHub App (for repository operations)
- Create a GitHub App (instructions)
- Webhook URL:
https://your-domain/sfp/api/webhook/github
- Webhook URL:
- Configure via the integration API (
POST /api/integrations) or add to.env:GITHUB_APP_ID=your-app-id GITHUB_APP_PRIVATE_KEY=your-private-key-pem
Slack, AI, and Other Integrations
Add the relevant environment variables to .env on the server and restart:
SLACK_APP_TOKEN,SLACK_SIGNING_SECRET,SLACK_BOT_TOKENOPENAI_API_KEY,AI_PROVIDER,AI_MODEL
Admin Dashboard Access (ALLOWED_IPS)
The following admin dashboards are IP-restricted by Caddy:
| Dashboard | Port | Purpose |
|---|---|---|
| Hatchet | 8080 | Workflow monitoring |
| Supabase Studio | 3100 | Database management |
| Verdaccio | 4873 | npm registry browser |
By default, only private network ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) can access these ports. To add your IP addresses, SSH to the server and edit .env:
# Single IP
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:
docker compose restart caddy
Updating
To update the server to a new version:
- Go to Actions > Update SFP Server
- Click Run workflow
- Optionally specify a Docker tag (defaults to
IMAGE_TAGvariable orlatest) - Configure drain and backup options as needed
- Click Run workflow
The update process:
- Backs up current configuration (unless skipped)
- Waits for active workflows to complete (unless skipped)
- Updates configuration files
- Caddy serves a maintenance page while app services restart
- Pulls new Docker images
- Starts services and runs database migrations
Version Checks
The Check Deployed Version workflow can be triggered manually to check what version is running on the server. It SSHs in and reads the IMAGE_TAG from the server's .env file, comparing it with the configured IMAGE_TAG variable.
Backup and Recovery
The server's .env file contains auto-generated credentials that are critical for operation:
- Supabase JWT secret and API keys
- Database passwords
- Hatchet workflow engine tokens
These are NOT stored in GitHub -- they live on the server.
We recommend:
- Regularly backing up the
.envfile from{BASE_DIR}/tenants/{TENANT_NAME}/.env - The
sfp server updatecommand automatically backs up configuration before each update (stored inbackups/on the server) - Store a copy of
.envin a secure location (e.g., password manager, secrets vault)
If the server is destroyed, you will need the backed-up .env to restore without re-initializing.
Troubleshooting
Workflow fails at image pull
- Verify
DOCKER_REGISTRY_TOKENis valid - Verify
DOCKER_REGISTRYandIMAGE_FQDNare correct - Check if the specified
IMAGE_TAGexists in the registry
Workflow fails at SSH connection
- Verify
SSH_PRIVATE_KEYis the full private key (including headers) - Verify
SSH_HOSTis reachable from GitHub Actions runners - Verify the SSH user has permission to run
dockercommands
Server not accessible after init
- Check port 443 is open in the firewall
- Verify DNS resolves to the server IP
- Check TLS certificate is valid for the domain
- SSH to the server and check logs:
docker compose logs caddy
File Reference
| File | Purpose |
|---|---|
.github/actions/setup-sfp/action.yml |
Composite action: pull Docker image + configure SSH |
.github/workflows/init.yml |
One-time server initialization workflow |
.github/workflows/start.yml |
Start server services |
.github/workflows/stop.yml |
Stop server services |
.github/workflows/update.yml |
Server update workflow |
.github/workflows/check-update.yml |
Check deployed version |
config/server-config.example.json |
Example JSON config for manual (non-workflow) init |
.env.template |
Reference for all configuration variables |