Skip to content

Self-Hosting

Fork-Based Deployment Model

Every application in the Towlion ecosystem is designed to be forkable and self-deployable. Instead of multi-tenant SaaS, the platform uses repository forks as the isolation boundary.

User forks repository
Configure deployment secrets
Push code
GitHub Actions deploys app
Application runs on user's server

Each fork runs independently — there is no shared tenancy or centralized hosting.

original repo
     ├─ fork by Alice → alice.example.com
     ├─ fork by Bob   → bob.example.com
     └─ fork by Carol → carol.example.com

Benefits:

  • Strong isolation between deployments
  • Simple architecture (no tenant management)
  • Full control for each operator

Server Requirements

Resource Minimum
CPU 2 cores
RAM 4 GB
Disk 50 GB
Data disk Mounted at /data

Required ports:

Port Purpose
22 SSH
80 HTTP
443 HTTPS

Required Secrets

Fork users must configure these GitHub Actions secrets:

Secret Purpose
SERVER_HOST Server IP address
SERVER_USER SSH user (typically deploy)
SERVER_SSH_KEY SSH private key for deployment
APP_DOMAIN Application domain name (e.g., app.example.com)

Note: Database and storage credentials are auto-generated by the bootstrap script on the server. They are not GitHub secrets.

Optional secrets:

Secret Purpose
PREVIEW_DOMAIN Base domain for preview environments (e.g., example.com)

Tip: To reduce the number of required secrets from 4 to 1, you can use a self-hosted GitHub Actions runner instead. See Self-Hosted Runners.

Bootstrap Process

To deploy an application from a fork:

  1. Fork the repository on GitHub
  2. Create a server (Debian, meeting the minimum requirements)
  3. Run the bootstrap script to install Docker and dependencies
  4. Configure DNS — point your domain to the server IP
  5. Configure GitHub secrets — add the required secrets to your fork
  6. Push code — deployment runs automatically via GitHub Actions

Bootstrap Script

The repository includes a single bootstrap script that transforms a fresh Debian server into a ready-to-deploy platform:

sudo ACME_EMAIL=you@example.com OPS_DOMAIN=ops.example.com bash infrastructure/bootstrap-server.sh

The script is idempotent — safe to re-run on an already-bootstrapped server. It performs:

  • System packages (git, curl, ufw, vnstat, unattended-upgrades)
  • Firewall configuration (ports 22, 80, 443)
  • Docker and Compose plugin installation
  • deploy user creation with SSH directory
  • Directory structure (/data/*, /opt/apps, /opt/platform)
  • Docker network (towlion) for cross-container communication
  • Credential generation (PostgreSQL, MinIO, and Grafana passwords in /opt/platform/.env)
  • Platform Caddyfile with per-app import pattern
  • Platform docker-compose.yml (PostgreSQL 16, Redis 7, MinIO, Caddy 2, Loki 3.0, Promtail 3.0, Grafana 11.0)
  • Service startup and verification
  • Infrastructure scripts and cron jobs (backups, alerts, image updates)

Post-Bootstrap Verification

After bootstrapping, run the verification script to confirm everything is working:

bash infrastructure/verify-server.sh

This performs a read-only check of all services, directories, and firewall rules.

Per-App Credentials

For each application, provision isolated database and storage credentials:

bash /opt/platform/infrastructure/create-app-credentials.sh <app-name>

This creates a dedicated PostgreSQL user and MinIO bucket. Credentials are written to /opt/platform/credentials/<app-name>.env and automatically picked up by the deploy workflow.

Monitoring (Optional)

If you set OPS_DOMAIN during bootstrap (or add it to /opt/platform/.env), Grafana is accessible at https://OPS_DOMAIN. The default admin credentials are in /opt/platform/.env. Configure DNS for the ops domain the same way as app domains.

DNS Configuration

Point your domain (or subdomain) to your server's IP address:

A record: app.example.com → SERVER_IP

For multiple applications, use subdomains:

uku.example.com   → SERVER_IP
timer.example.com → SERVER_IP

Caddy handles TLS certificate provisioning automatically once DNS is configured.

What You Get

After deployment, your server runs:

  • Your application (FastAPI backend + Next.js frontend)
  • PostgreSQL database
  • Redis cache and queue
  • MinIO object storage
  • Caddy reverse proxy with automatic TLS
  • Celery background workers

All managed through GitHub — push code to deploy updates.

For the full directory layout, lifecycle sequence, and workflow assumptions, see Server Contract.