Self-Hosting
Overview
Section titled “Overview”Frontman uses a split architecture:
- Client libraries (browser-side MCP servers) — bundled into your app via npm packages (
@frontman-ai/nextjs,@frontman-ai/astro,@frontman-ai/vite) - Server (AI agent orchestration) — Elixir/Phoenix application that queries MCP tools, generates edits, and writes source files
For local development, the server runs at api.frontman.sh (our hosted instance). Self-hosting is only needed if you want to run your own instance of the orchestration server — for data sovereignty, air-gapped environments, or custom modifications.
Architecture
Section titled “Architecture”┌─────────────────────────────────────────────────┐│ Your Machine ││ ┌─────────────────┐ ┌────────────────────────┐ ││ │ Dev Server │ │ Browser │ ││ │ (Next/Astro/ │ │ (Frontman overlay) │ ││ │ Vite) │ │ │ ││ │ │ │ Browser-side MCP Server │ ││ │ Server-side │ │ (DOM, CSS, screenshots) │ ││ │ MCP tools │ └───────────┬─────────────┘ ││ │ (routes, logs, │ │ ││ │ build errors) │ │ ││ └────────┬────────┘ │ │└──────────┼───────────────────────┼───────────────┘ │ │ │ WebSocket/HTTPS │ └───────────┬───────────┘ │┌──────────────────────┼───────────────────────────┐│ Frontman Server │ (Elixir/Phoenix) ││ ┌───────────────────▼──────────────────────┐ ││ │ AI Agent Orchestrator │ ││ │ - Queries MCP tools (browser + server) │ ││ │ - Calls LLM (Claude/GPT/OpenRouter) │ ││ │ - Generates code edits │ ││ │ - Writes to source files via MCP │ ││ │ - Triggers hot reload │ ││ └──────────────────────────────────────────┘ ││ ││ PostgreSQL (user accounts, task history) ││ Oban (background jobs: email, webhooks) ││ OpenTelemetry (optional observability) │└───────────────────────────────────────────────────┘The server is stateless except for PostgreSQL. Horizontal scaling is supported via Elixir’s distributed runtime (RELEASE_DISTRIBUTION=name).
Requirements
Section titled “Requirements”Runtime
Section titled “Runtime”- Elixir 1.19+ and Erlang/OTP 28+ (BEAM VM)
- PostgreSQL 17+ (for user accounts and task history)
- Node.js 24+ (for building assets — not needed in production)
- Linux x86_64 or ARM64 (Ubuntu 24.04 LTS recommended)
Build-time (optional, for Docker or releases)
Section titled “Build-time (optional, for Docker or releases)”- Yarn 4+ (monorepo dependency management)
- ReScript 12+ (client library compilation)
Resources (production)
Section titled “Resources (production)”- Minimum: 1 vCPU, 2GB RAM, 20GB disk
- Recommended: 2 vCPU, 4GB RAM, 50GB disk (allows PostgreSQL tuning and room for logs)
- Storage: Database grows with task history. Plan ~1GB/month for 100 active users. Backups require 2x database size.
Network
Section titled “Network”- Outbound HTTPS to LLM APIs (Anthropic, OpenAI, OpenRouter)
- Inbound HTTPS (443) for client connections
- WebSocket support required (Phoenix Channels)
Deployment Options
Section titled “Deployment Options”Option 1: Bare Metal / VM (Recommended for Production)
Section titled “Option 1: Bare Metal / VM (Recommended for Production)”This is how we run api.frontman.sh — a single Hetzner server with blue/green deploys.
Prerequisites:
- Ubuntu 24.04 LTS server
- DNS A record pointing to server IP (we use Cloudflare DNS-only mode)
- SSH access as root
Setup:
# 1. Run server setup script (installs PostgreSQL, Caddy, systemd services)ssh root@<server-ip> 'bash -s' < infra/production/server-setup.sh
# 2. Fill in environment secretsssh deploy@<server-ip>nano /opt/frontman/blue/env# Set CLOAK_KEY, WORKOS_API_KEY, WORKOS_CLIENT_ID, etc.
# 3. Deploy first release (GitHub Actions workflow builds and deploys on push to main)# Or manually:git clone https://github.com/frontman-ai/frontman.gitcd frontmanmake -C apps/frontman_server releasescp apps/frontman_server/_build/prod/frontman_server-*.tar.gz deploy@<server>:/tmp/ssh deploy@<server> '/opt/frontman/deploy.sh /tmp/frontman_server-*.tar.gz'What the setup script does:
- Installs PostgreSQL 17 with production tuning (auto-detects RAM)
- Creates
frontmandatabase user andfrontman_server_proddatabase - Installs Caddy reverse proxy (auto TLS via Let’s Encrypt)
- Creates
/opt/frontman/{blue,green}directories for blue/green deploys - Installs systemd services (
frontman-blue.service,frontman-green.service) - Sets up nightly PostgreSQL backups (cron at 3:00 AM)
- Configures UFW firewall (22, 80, 443) and fail2ban
Blue/Green Deployment:
The deploy script (/opt/frontman/deploy.sh) extracts the release to the inactive slot, runs migrations, smoke tests the new version, then swaps Caddy’s upstream. Zero-downtime deploys. Rollback via /opt/frontman/rollback.sh.
Monitoring (optional):
# Install Prometheus, Alertmanager, Blackbox Exporterssh root@<server> 'bash -s' < infra/production/monitoring/setup-monitoring.shExports to Arize via OpenTelemetry if ARIZE_API_KEY and ARIZE_SPACE_ID are set in env.
Option 2: Docker
Section titled “Option 2: Docker”Build the image:
cd apps/frontman_serverdocker build -t frontman-server .Run (single instance):
docker run -d \ --name frontman-server \ -p 4000:4000 \ -e DATABASE_URL='ecto://user:pass@postgres-host/frontman_server_prod' \ -e SECRET_KEY_BASE='<generate via: mix phx.gen.secret>' \ -e CLOAK_KEY='<generate via: openssl rand -base64 32>' \ -e WORKOS_API_KEY='<your-workos-api-key>' \ -e WORKOS_CLIENT_ID='<your-workos-client-id>' \ frontman-serverEnvironment variables: See infra/production/env.template for full list. Required:
DATABASE_URL— PostgreSQL connection stringSECRET_KEY_BASE— Session encryption (generate:mix phx.gen.secret)CLOAK_KEY— API key encryption at rest (generate:openssl rand -base64 32)WORKOS_API_KEY,WORKOS_CLIENT_ID— OAuth (GitHub, Google login)
Optional:
ARIZE_API_KEY,ARIZE_SPACE_ID— OpenTelemetry exportDISCORD_NEW_USERS_WEBHOOK_URL— New user signup notificationsRESEND_API_KEY— Email delivery (welcome emails, password resets)
PostgreSQL setup:
# Run postgres containerdocker run -d \ --name frontman-postgres \ -e POSTGRES_DB=frontman_server_prod \ -e POSTGRES_USER=frontman \ -e POSTGRES_PASSWORD='<random-password>' \ -v frontman-pg-data:/var/lib/postgresql/data \ postgres:17-alpine
# Run migrations (first time only)docker exec frontman-server /app/bin/frontman_server eval "FrontmanServer.Release.migrate()"Option 3: From Source (Development)
Section titled “Option 3: From Source (Development)”Prerequisites:
- Elixir 1.19+, Erlang 28+, Node.js 24+, PostgreSQL 17+
- Yarn 4+ (enable via
corepack enable)
Setup:
git clone https://github.com/frontman-ai/frontman.gitcd frontman
# 1. Install dependenciesyarn installcd apps/frontman_servermix deps.get
# 2. Create databasemix ecto.createmix ecto.migrate
# 3. Install assets (esbuild, tailwind)mix setup
# 4. Configure environmentcp envs/.dev.env envs/.dev.local.env# Edit .dev.local.env with your API keys
# 5. Start servermix phx.server# Visit http://localhost:4000Configuration: Uses Dotenvy to load env files in this order:
envs/.env(base config, checked into git)envs/.dev.env(dev defaults)envs/.dev.overrides.env(local overrides, gitignored)- System environment variables (highest precedence)
Secrets (WorkOS keys, LLM API keys) are stored in envs/.dev.secrets.env as op:// references (1Password CLI). The Makefile wraps mix phx.server with op run --env-file=envs/.dev.secrets.env to inject them at runtime. If you don’t use 1Password, set them directly in .dev.overrides.env.
Configuration
Section titled “Configuration”Required Environment Variables
Section titled “Required Environment Variables”Application
Section titled “Application”PHX_HOST— Public hostname (e.g.,api.frontman.sh)PHX_SERVER=true— Start Phoenix HTTP endpointPORT— HTTP port (default: 4000)
Database
Section titled “Database”DATABASE_URL— PostgreSQL connection string- Format:
ecto://user:pass@host/database - Example:
ecto://frontman:secretpass@localhost/frontman_server_prod
- Format:
DATABASE_SSL— Enable SSL (default: true in prod)- Set to
falsefor local PostgreSQL without SSL
- Set to
Security
Section titled “Security”SECRET_KEY_BASE— Phoenix session encryption- Generate:
mix phx.gen.secret - Must be 64+ characters
- Generate:
CLOAK_KEY— API key encryption at rest (Cloak Ecto)- Generate:
openssl rand -base64 32
- Generate:
Authentication (WorkOS)
Section titled “Authentication (WorkOS)”Frontman uses WorkOS for OAuth (GitHub, Google login). Required for production:
WORKOS_API_KEY— WorkOS API secretWORKOS_CLIENT_ID— WorkOS OAuth client ID
Get these from WorkOS Dashboard.
Optional Environment Variables
Section titled “Optional Environment Variables”Observability (OpenTelemetry → Arize)
Section titled “Observability (OpenTelemetry → Arize)”ARIZE_API_KEY— Arize Phoenix API keyARIZE_SPACE_ID— Arize space IDARIZE_COLLECTOR_ENDPOINT— Default:https://otlp.eu-west-1a.arize.comARIZE_PROJECT_NAME— Default:frontman
If both API key and space ID are set, spans are exported to Arize. Otherwise, telemetry collection is disabled.
Notifications
Section titled “Notifications”DISCORD_NEW_USERS_WEBHOOK_URL— Discord webhook for new user signups (omit to disable)
Email (Resend)
Section titled “Email (Resend)”RESEND_API_KEY— Required for welcome emails and password resets in production
Database Tuning
Section titled “Database Tuning”POOL_SIZE— Ecto connection pool size (default: 10)ECTO_IPV6— Set totrueor1to use IPv6
Clustering (Distributed Elixir)
Section titled “Clustering (Distributed Elixir)”RELEASE_NODE— Node name (e.g.,frontman@127.0.0.1)RELEASE_COOKIE— Erlang distribution cookie (shared secret for clustering)RELEASE_DISTRIBUTION=name— Enable distributed modeDNS_CLUSTER_QUERY— DNS SRV query for node discovery (e.g.,_frontman._tcp.internal.local)
Database Management
Section titled “Database Management”Migrations
Section titled “Migrations”# Production (inside release)/app/bin/frontman_server eval "FrontmanServer.Release.migrate()"
# Developmentmix ecto.migrateBackups
Section titled “Backups”The setup script installs a daily backup cron (3:00 AM) that dumps PostgreSQL to /opt/frontman/backups/daily/. Retention: 7 days. Adjust in /opt/frontman/backup-pg.sh.
Manual backup:
pg_dump -U frontman frontman_server_prod | gzip > backup-$(date +%Y%m%d).sql.gzRestore:
gunzip < backup-YYYYMMDD.sql.gz | psql -U frontman frontman_server_prodRollback
Section titled “Rollback”# Via deploy script (rolls back to previous release)ssh deploy@<server> '/opt/frontman/rollback.sh'
# Manual Ecto rollback (dev)mix ecto.rollback --step 1Security Considerations
Section titled “Security Considerations”Data Flow
Section titled “Data Flow”- Source code: Never uploaded to the server. The browser-side MCP server reads files locally and sends diffs to the server. The server writes patches back via MCP file write tools.
- Secrets: LLM API keys are encrypted at rest in PostgreSQL using Cloak Ecto (AES-256-GCM). The
CLOAK_KEYenv var decrypts them. - OAuth tokens: WorkOS handles GitHub/Google OAuth. Frontman receives an auth code, exchanges it for user info, and stores a session cookie (Phoenix signed sessions).
Transport Security
Section titled “Transport Security”- HTTPS required in production. Caddy auto-provisions Let’s Encrypt TLS certificates.
- WebSocket encryption: Phoenix Channels run over WSS (WebSocket Secure) in production.
Authentication
Section titled “Authentication”- OAuth via WorkOS — GitHub and Google SSO
- Session-based — Phoenix signed cookies (
SECRET_KEY_BASE) - No password storage — OAuth-only (no email/password login)
Firewall
Section titled “Firewall”The setup script configures UFW to allow only SSH (22), HTTP (80), HTTPS (443). PostgreSQL (5432) is bound to localhost only.
Secrets Management
Section titled “Secrets Management”Never commit secrets to git. Use:
- Environment files (
.env) for non-sensitive config (gitignored) - Secret managers (1Password CLI, AWS Secrets Manager, HashiCorp Vault) for production
Example with 1Password CLI (used in our dev workflow):
WORKOS_API_KEY=op://vault/WorkOS/API_Key
# Run server with secrets injectedop run --env-file=envs/.dev.secrets.env mix phx.serverMonitoring & Observability
Section titled “Monitoring & Observability”Health Checks
Section titled “Health Checks”- HTTP:
GET /api/health→{"status": "ok"} - Database: Phoenix Dashboard at
/dashboard(dev only)
- Development: Console output (colorized via Phoenix Logger)
- Production: Systemd journal (
journalctl -u frontman-blue -f) - Structured logging: JSON format via Logger (configurable in
config/prod.exs)
Metrics (Prometheus)
Section titled “Metrics (Prometheus)”Optional setup script installs Prometheus + Alertmanager + Blackbox Exporter. Scrapes:
- BEAM VM metrics (via
TelemetryMetrics) - HTTP request rates, latencies (Phoenix instrumentation)
- Database query timing (Ecto telemetry)
- Health endpoint uptime (Blackbox Exporter)
Alerts fire to Alertmanager → Discord webhook on:
- Instance down (health check fails for 1 minute)
- High error rate (>5% 5xx responses over 5 minutes)
- Database connection pool exhausted
Distributed Tracing
Section titled “Distributed Tracing”If ARIZE_API_KEY and ARIZE_SPACE_ID are set, OpenTelemetry spans are exported to Arize Phoenix for LLM observability (prompt tracing, token usage, latency).
Scaling & High Availability
Section titled “Scaling & High Availability”Horizontal Scaling
Section titled “Horizontal Scaling”Frontman is stateless except for PostgreSQL. To scale horizontally:
- Run multiple instances behind a load balancer (e.g., Caddy with
lb_policy round_robin) - Shared PostgreSQL — all instances connect to the same database
- Distributed Elixir — set
RELEASE_NODE,RELEASE_COOKIE,RELEASE_DISTRIBUTION=name, andDNS_CLUSTER_QUERYfor node discovery
Example (3 nodes):
# Node 1RELEASE_NODE=frontman1@10.0.1.10 RELEASE_COOKIE=secret /app/bin/server
# Node 2RELEASE_NODE=frontman2@10.0.1.11 RELEASE_COOKIE=secret /app/bin/server
# Node 3RELEASE_NODE=frontman3@10.0.1.12 RELEASE_COOKIE=secret /app/bin/serverNodes will form a cluster. Phoenix PubSub messages (task updates, hot reload triggers) are distributed across all nodes.
Database HA
Section titled “Database HA”For production, use:
- PostgreSQL replication (streaming replication + failover via Patroni or Stolon)
- Managed databases (AWS RDS, GCP Cloud SQL, Azure PostgreSQL)
Backup Strategy
Section titled “Backup Strategy”- Automated daily backups (setup script installs cron)
- Offsite storage (rsync to S3, GCS, or Backblaze B2)
- Test restores regularly (quarterly is recommended)
Commercial Considerations
Section titled “Commercial Considerations”Licensing
Section titled “Licensing”- Client libraries (
libs/) — Apache 2.0 (permissive, commercial use allowed) - Server (
apps/frontman_server/) — AGPL-3.0 (copyleft, requires source disclosure if distributed)
If you self-host, you must:
- Provide source code to users who interact with your modified server (AGPL network clause)
- Disclose modifications if you distribute the server
Commercial licenses available — contact us if AGPL doesn’t fit your use case (e.g., SaaS white-label, proprietary forks). See AI-SUPPLEMENTARY-TERMS.md for AI training restrictions.
Support
Section titled “Support”Self-hosted deployments are community-supported via:
- GitHub Issues (bug reports, feature requests)
- Discord (community help)
Enterprise support available (SLA-backed, private Slack channel, dedicated engineer). Includes:
- Custom deployment consulting
- Priority bug fixes and backports
- Security patches with 24-hour SLA
- Performance tuning and database optimization
Contact enterprise@frontman.sh for pricing.
Cloud Marketplaces
Section titled “Cloud Marketplaces”Not currently available on AWS/GCP/Azure marketplaces. Roadmap item for 2025. Track progress in issue #123.
Multi-Tenancy
Section titled “Multi-Tenancy”The hosted api.frontman.sh runs a single instance serving all users. Self-hosted deployments can run:
- Single-tenant (one org, one database)
- Multi-tenant (multiple orgs, shared database with row-level security)
Multi-tenancy is implemented via organization_id foreign keys + Ecto query scoping. See apps/frontman_server/lib/frontman_server/organizations.ex for details.
Cost Estimates
Section titled “Cost Estimates”Hosted (api.frontman.sh)
Section titled “Hosted (api.frontman.sh)”- Free — no subscription fees, no usage limits
- BYOK — you pay your LLM provider directly (Anthropic, OpenAI, OpenRouter) at standard API rates
Self-Hosted (estimated monthly costs)
Section titled “Self-Hosted (estimated monthly costs)”- Hetzner CCX13 (2 vCPU, 8GB RAM, 80GB NVMe) — €15/month (~$16 USD)
- PostgreSQL (included on VM)
- Backups (Hetzner Backup +20%) — €3/month (~$3 USD)
- Total:
€18/month ($19 USD)
Compare to:
- Cursor Pro — $20/month/user
- GitHub Copilot Pro — $10/month/user
- v0 Premium — $20/month/user
Self-hosting is cost-effective for teams of 2+ users.
Troubleshooting
Section titled “Troubleshooting”Server won’t start
Section titled “Server won’t start”# Check systemd logsjournalctl -u frontman-blue -f
# Common issues:# 1. DATABASE_URL incorrect → check /opt/frontman/blue/env# 2. SECRET_KEY_BASE missing → generate: mix phx.gen.secret# 3. Port already in use → check: sudo lsof -i :4000Database connection errors
Section titled “Database connection errors”# Test PostgreSQL connectionpsql -U frontman -h localhost frontman_server_prod
# Check PostgreSQL statussystemctl status postgresql
# Check pg_hba.conf authsudo cat /etc/postgresql/17/main/pg_hba.conf | grep frontmanMigrations fail
Section titled “Migrations fail”# Run manually with debug output/app/bin/frontman_server eval "FrontmanServer.Release.migrate()" --verbose
# Rollback and retrymix ecto.rollback --step 1mix ecto.migrateWebSocket connections drop
Section titled “WebSocket connections drop”- Caddy timeout: Increase
timeoutin Caddyfile (default: 5 minutes) - Firewall: Ensure TCP 443 allows long-lived connections (disable stateful inspection if needed)
Out of memory (OOM)
Section titled “Out of memory (OOM)”# Check BEAM memory usage/app/bin/frontman_server remote
# In IEx console::erlang.memory()# Look for 'total' — should be < 80% of available RAM
# Tune VM (add to env):ERL_AFLAGS="+MBas aoffcbf +MBac false +MBlmbcs 512"High database load
Section titled “High database load”# Check slow queries (inside psql):SELECT * FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;
# Add indexes (migrations in apps/frontman_server/priv/repo/migrations/)# Run: mix ecto.gen.migration add_index_to_tasksUpgrade Guide
Section titled “Upgrade Guide”Minor Versions (0.x.y → 0.x.z)
Section titled “Minor Versions (0.x.y → 0.x.z)”Safe to deploy without downtime (backward-compatible migrations):
# Deploy via GitHub Actions (push to main)git push origin main
# Or manually:ssh deploy@<server> '/opt/frontman/deploy.sh /tmp/new-release.tar.gz'Major Versions (0.x → 1.0)
Section titled “Major Versions (0.x → 1.0)”May require manual migration steps:
- Read CHANGELOG.md for breaking changes
- Backup database (
/opt/frontman/backup-pg.sh) - Deploy to green slot (test before swapping)
- Run migrations (deploy script does this automatically)
- Smoke test (deploy script checks health endpoint)
- Swap Caddy upstream (deploy script updates Caddyfile)
Rollback if needed: /opt/frontman/rollback.sh
Do I need to self-host?
Section titled “Do I need to self-host?”No — most users should use api.frontman.sh (our hosted instance). Self-host only if you need data sovereignty, air-gapped environments, or custom server modifications.
Can I run Frontman without a server?
Section titled “Can I run Frontman without a server?”No — the server orchestrates AI agent execution. The client libraries (Next.js/Astro/Vite plugins) are just MCP servers that expose dev server context to the agent.
Is the database required?
Section titled “Is the database required?”Yes — stores user accounts, OAuth tokens, task history, and encrypted API keys.
Can I use MySQL instead of PostgreSQL?
Section titled “Can I use MySQL instead of PostgreSQL?”No — Ecto migrations use PostgreSQL-specific features (JSONB, UUID extensions). MySQL support is not planned.
How do I change the domain?
Section titled “How do I change the domain?”- Update
PHX_HOSTin/opt/frontman/blue/envand/opt/frontman/green/env - Update Caddy config:
/etc/caddy/Caddyfile - Reload Caddy:
sudo systemctl reload caddy - Update DNS A record to point to your server
Can I disable OAuth and use email/password?
Section titled “Can I disable OAuth and use email/password?”Not currently supported. OAuth via WorkOS is the only auth method. Email/password login is a roadmap item for 2025 (track in issue #456).
How do I add a new LLM provider?
Section titled “How do I add a new LLM provider?”See Adding LLM Providers guide. Requires:
- Add provider config to
apps/frontman_server/config/config.exs - Implement API adapter in
apps/frontman_server/lib/frontman_server/providers/ - Add OAuth flow if needed (WorkOS or custom)
Resources
Section titled “Resources”For enterprise support or commercial licensing questions, contact enterprise@frontman.sh.