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:

  1. Linux server -- x86_64, 8+ vCPU, 32+ GB RAM, 250+ GB SSD
  2. Docker Engine 24+ and Docker Compose v2 installed on the server
  3. Domain name (FQDN) resolving to the server
  4. SSH access to the server from GitHub Actions runners
  5. Docker registry token for pulling SFP server images (the CLI runs from inside the image)
  6. 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

  1. Go to Actions > Initialize SFP Server
  2. Click Run workflow
  3. Select the environment and TLS mode (see below)
  4. 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 email
  • adminPassword -- admin login password
  • studioUser / 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)

  1. Create a GitHub OAuth App (instructions)
    • Homepage URL: https://your-domain
    • Callback URL: https://your-domain/auth/v1/callback
  2. 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
    
  3. Restart services: docker compose restart supabase-auth

GitHub App (for repository operations)

  1. Create a GitHub App (instructions)
    • Webhook URL: https://your-domain/sfp/api/webhook/github
  2. 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_TOKEN
  • OPENAI_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:

  1. Go to Actions > Update SFP Server
  2. Click Run workflow
  3. Optionally specify a Docker tag (defaults to IMAGE_TAG variable or latest)
  4. Configure drain and backup options as needed
  5. Click Run workflow

The update process:

  1. Backs up current configuration (unless skipped)
  2. Waits for active workflows to complete (unless skipped)
  3. Updates configuration files
  4. Caddy serves a maintenance page while app services restart
  5. Pulls new Docker images
  6. 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 .env file from {BASE_DIR}/tenants/{TENANT_NAME}/.env
  • The sfp server update command automatically backs up configuration before each update (stored in backups/ on the server)
  • Store a copy of .env in 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_TOKEN is valid
  • Verify DOCKER_REGISTRY and IMAGE_FQDN are correct
  • Check if the specified IMAGE_TAG exists in the registry

Workflow fails at SSH connection

  • Verify SSH_PRIVATE_KEY is the full private key (including headers)
  • Verify SSH_HOST is reachable from GitHub Actions runners
  • Verify the SSH user has permission to run docker commands

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
S
Description
Template for sfp-server-mangement using github actions
Readme 89 KiB