2026-04-14 11:08:06 +10:00
# 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
2026-04-14 14:24:16 +10:00
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.
2026-04-14 11:08:06 +10:00
## 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
2026-04-14 14:24:16 +10:00
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
2026-04-14 11:08:06 +10:00
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 |
2026-04-14 11:20:31 +10:00
| `IMAGE_FQDN` | Yes | -- | Full Docker image path (e.g., `ghcr.io/flxbl-io/sfp-server-rc` ) |
2026-04-14 11:08:06 +10:00
| `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 **
2026-04-14 14:24:16 +10:00
3. Select the **environment ** and **TLS mode ** (see below)
2026-04-14 11:08:06 +10:00
4. Click **Run workflow **
2026-04-14 14:24:16 +10:00
#### 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 |
2026-04-14 11:08:06 +10:00
The init process will:
2026-04-14 11:20:31 +10:00
- Pull the SFP server Docker image (the CLI runs from inside it)
2026-04-14 11:08:06 +10:00
- 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
2026-04-14 14:24:16 +10:00
### 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 <user>@<host> "cat <base-dir>/tenants/<tenant>/credentials.json"
```
2026-04-14 11:08:06 +10:00
2026-04-14 14:24:16 +10:00
The file contains:
- `adminEmail` -- admin login email
- `adminPassword` -- admin login password
- `studioUser` / `studioPassword` -- Supabase Studio dashboard credentials
2026-04-14 11:08:06 +10:00
2026-04-14 14:24:16 +10:00
### 6. Verify
Open `https://your-domain` (or `http://your-domain` if using `tls-mode=none` ) in a browser to verify the server is accessible.
2026-04-14 11:08:06 +10:00
## 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`
2026-04-14 14:24:16 +10:00
## 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
` ``
2026-04-14 11:08:06 +10:00
## 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
2026-04-14 14:24:16 +10:00
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.
2026-04-14 11:08:06 +10:00
## 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
2026-04-14 11:20:31 +10:00
### 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
2026-04-14 11:08:06 +10:00
### 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 |
|------|---------|
2026-04-14 11:20:31 +10:00
| ` .github/actions/setup-sfp/action.yml` | Composite action: pull Docker image + configure SSH |
2026-04-14 11:08:06 +10:00
| ` .github/workflows/init.yml` | One-time server initialization workflow |
2026-04-14 14:24:16 +10:00
| ` .github/workflows/start.yml` | Start server services |
| ` .github/workflows/stop.yml` | Stop server services |
2026-04-14 11:08:06 +10:00
| ` .github/workflows/update.yml` | Server update workflow |
2026-04-14 14:24:16 +10:00
| ` .github/workflows/check-update.yml` | Check deployed version |
2026-04-14 11:08:06 +10:00
| ` config/server-config.example.json` | Example JSON config for manual (non-workflow) init |
| ` .env.template` | Reference for all configuration variables |