Self-host & deployment
Run the entire Appbricx platform on infrastructure you control — your Linux host, your database, your domain. Every service (API, WebSocket, Next.js dashboard, PostgreSQL, Caddy edge) ships as a Docker container wired together by a single docker-compose.yml.
What you need
- Linux host. Ubuntu 22.04 or 24.04 LTS, x86_64, 2 vCPU and 2 GB RAM minimum (4 GB recommended for Puppeteer thumbnails and multiple per-project runtimes). 20 GB disk minimum.
- Docker + Docker Compose. The compose stack is the only supported install path for production self-host. macOS and Windows with Docker Desktop / WSL2 work for local trials.
- A domain you control (e.g.
app.example.com) with DNS pointing at the host, or a Cloudflare Tunnel if you would rather not expose ports directly. - Inbound ports 80 and 443open on the host for Caddy's ACME challenge and HTTPS traffic. All other ports stay on the docker internal network.
- Root SSH access during install — the setup script writes systemd units, tweaks UFW, and configures PostgreSQL.
One-command install
Clone the repo and run the setup script from the docker directory. The script generates a .env with strong random secrets, provisions PostgreSQL, and brings the entire stack up:
git clone https://github.com/appbricx-me/appbricx.git
cd appbricx
./deployment/docker/setup.sh
docker compose --env-file deployment/docker/.env up -dFor a public VPS with a real domain, pass the domain and an ACME email so Caddy fetches a Let's Encrypt certificate on first boot:
DOMAIN=app.example.com EMAIL=you@example.com \
./deployment/docker/setup.shBehind Cloudflare Tunnel or another reverse proxy, add --skip-ssl so Caddy binds only to 127.0.0.1 and uses an internal self-signed cert for the tunnel-to-origin hop:
DOMAIN=app.example.com ./deployment/docker/setup.sh --skip-sslFirst boot takes a few minutes while Postgres initialises, migrations run, and images are pulled or built. Tail the logs to watch progress:
docker compose --env-file deployment/docker/.env logs -fDocker Compose services
The default deployment/docker/docker-compose.yml defines six services on a shared internal network:
| Service | What it does |
|---|---|
postgres | pgvector/pgvector:pg16 — main database with pgvector, pg_trgm, and pgcrypto extensions. No host port published; reached only over the docker internal network. |
migrate | One-shot container that runs schema migrations against Postgres on every up, then exits. Uses the owning role for DDL; runtime services use a lower-privileged role. |
api | Hono API server — auth, project runtime, workflows, REST, CDC outbox. Reached through Caddy at /api/*. |
ws | Yjs CRDT WebSocket server for live collaborative editing. Reached through Caddy at /ws. |
web | Next.js dashboard, editor, and docs. Reached through Caddy at /. |
caddy | TLS termination and reverse proxy. Fetches Let's Encrypt certificates automatically, or serves mkcert-issued local certs for desktop installs. |
Optional stacks (layer on top)
The repo ships extra compose files you can layer on for production hardening. Enable them with -f:
docker-compose.replica.yml— streaming read replica for Postgres (hot standby).docker-compose.walg.yml— WAL-G continuous archiving to S3-compatible storage for point-in-time recovery.docker-compose.backup.yml— scheduledpg_dumpsidecar that writes compressed dumps to a host-mounted volume.docker-compose.secure.yml— full hardened install based onDockerfile.secure(per-project systemd units, dovault sandbox, egress allow-list). Same security posture as a bare-metalsetup-server.shinstall.
First-run bootstrap: become the platform admin
Once docker compose ps shows every service healthy, open the dashboard in a browser and sign up. There are two supported ways to claim the platform-admin role:
- First signup wins. With no accounts in the database yet, the first user that completes signup is promoted to platform admin automatically. This is the easy path for solo self-host — open the URL yourself and sign up before anyone else can reach the box.
- Bootstrap token. Set
INSTALL_BOOTSTRAP_TOKEN(and optionallyINSTALL_BOOTSTRAP_TOKEN_EXPIRES_AT) in the environment before first boot. Whichever signup presents the matching token is promoted to admin, even if other users have already signed up. Useful when the platform lives behind a shared onboarding URL.
Both paths are handled by the API's first-user bootstrap logic — after the first admin exists, every subsequent signup joins as a regular workspace member unless the admin invites them explicitly.
TLS and custom domain
Caddy inside the stack handles all TLS. Three env vars in deployment/docker/.env (written by setup.sh) tell it what to do:
| Env var | Meaning |
|---|---|
APPBRICX_SITE | Hostname the browser hits (e.g. app.example.com). |
APPBRICX_TLS | An email for Let's Encrypt ACME, or /certs/cert.pem /certs/key.pem for local mkcert certs, or internal for self-signed. |
APPBRICX_BIND_ADDR | 0.0.0.0 for direct public ingress, or 127.0.0.1 when running behind a tunnel/proxy. |
The committed Caddyfile reads these via env-var substitution, so a single static config covers every scenario. If you need to serve tenant subdomains through the API (multi-tenant custom-domain mode), the repo also ships Caddyfile.tenants-via-api as an alternative — swap it in via a volume mount.
Backups
A daily pg_dump is the minimum recommended backup. The bare-metal cron line from the deployment guide:
0 3 * * * sudo -u postgres pg_dump appbricx \
| gzip > /root/backups/appbricx-$(date +%F).sql.gzFor docker installs, run the same dump from the postgres container:
docker compose exec -T postgres \
pg_dump -U appbricx appbricx | gzip > appbricx-$(date +%F).sql.gzAlso back up /data/sites/(published static bundles) and each project's source tree — these live on named docker volumes (appbricx_sites, api_projects, api_thumbnails) and are not in Postgres.
For point-in-time recovery, layer docker-compose.walg.yml to stream WAL segments off-box, then use deployment/docker/restore-pitr.sh to restore to a specific timestamp.
Read replicas and WAL-G
Two optional compose files cover Postgres high-availability and off-site backup:
docker-compose.replica.ymlplus thedeployment/docker/replica/config directory brings up a streaming hot standby. Point read-only workloads at it, or use it as a fast failover target.docker-compose.walg.ymlplusdeployment/docker/walg/configures WAL-G to push base backups and continuous WAL segments to S3-compatible object storage. Combine withrestore-pitr.shfor point-in-time recovery.
Both stacks are documented alongside the compose files. Layer them with docker compose -f docker-compose.yml -f docker-compose.walg.yml up -d.
Upgrading
For pre-built images from GHCR:
docker compose --env-file deployment/docker/.env pull
docker compose --env-file deployment/docker/.env up -dMigrations run automatically on next boot — the migrate service is idempotent and skips anything already applied. Named volumes are preserved across the restart.
For source builds, git pull and then rebuild:
git pull
docker compose --env-file deployment/docker/.env up -d --buildEnv vars checklist
setup.sh generates strong random values for the security secrets. If you configure by hand, these are the ones that must be set:
| Variable | Purpose |
|---|---|
DATABASE_URL | Postgres connection string for the API and WS containers. In the default stack this is postgres://appbricx_app:...@postgres:5432/appbricx. |
JWT_SECRET | Signs the platform's session and access tokens. Rotate and every user is logged out. |
ENCRYPTION_KEY | Symmetric key for encrypting per-workspace secrets at rest (OAuth tokens, workflow secrets, etc.). |
APPBRICX_KEK | Key-encryption-key that wraps ENCRYPTION_KEY and any tenant-scoped data keys. Store this outside the box (secrets manager, KMS) if you can. |
INTERNAL_SECRET | Shared secret for service-to-service calls (API talking to WS, published-app runtime calling back to the API). Never exposed to the browser. |
INSTALL_BOOTSTRAP_TOKEN | Optional. If set, whichever signup presents this token is promoted to platform admin. See the bootstrap section above. |
Optional but common additions to .env: Google and GitHub OAuth client id/secret pairs, an ANTHROPIC_API_KEY or OPENAI_API_KEY for platform-owned AI features (users can also bring their own — see BYOK), and Stripe keys if you plan to bill from your own account.
Support
- Custom Deployment track. If you are on the Custom Deployment tier we install and operate the stack for you (VPC, on-prem, or dedicated hardware) and stand behind an SLA. Reach out at contact@appbricx.com.
- BYOK self-host. Community support only — open an issue on the GitHub repo, or ask in the community forum. We read both. The compose files, setup scripts, and
deployment/RUNBOOK.mdin the repo are the source of truth for day-two operations.