# 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, download the SFP CLI from Gitea, and run the appropriate server lifecycle commands. ## 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. **TLS certificate + private key** (PEM format) -- or use Let's Encrypt for automatic TLS 5. **SSH access** to the server from GitHub Actions runners 6. **Docker registry token** for pulling SFP server images (the CLI runs from inside the image) 7. **Port 443** open on the server firewall 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 TLS mode (`custom` or `letsencrypt`) 4. Click **Run workflow** 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 - Start all services - Create the default admin user The admin credentials will be displayed in the workflow output. ### 5. Verify Open `https://your-domain` 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` ## 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 for Updates** workflow runs weekly (Monday 8 AM UTC) and: - Queries Gitea for the latest release - Compares with the currently deployed version - Creates a GitHub Issue if a newer version is available You can also trigger it manually from **Actions** > **Check for Updates**. ## 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/update.yml` | Server update workflow | | `.github/workflows/check-update.yml` | Weekly version check workflow | | `config/server-config.example.json` | Example JSON config for manual (non-workflow) init | | `.env.template` | Reference for all configuration variables |