# 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](https://source.flxbl.io/flxbl/sfp-pro/src/branch/main/docs/self-hosting-prerequisites.md) 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: ```bash ssh @ "cat /tenants//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](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app)) - **Homepage URL**: `https://your-domain` - **Callback URL**: `https://your-domain/auth/v1/callback` 2. SSH to the server and add to `.env`: ```bash 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](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app)) - **Webhook URL**: `https://your-domain/sfp/api/webhook/github` 2. Configure via the integration API (`POST /api/integrations`) or add to `.env`: ```bash 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`: ```bash # 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: ```bash 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 |