Deploy an app
The Deploy page — pick a one-click template or build from a repo, set the domain, port and readiness gate, then watch the live build→run→route on the deployment detail page.
Deploy an app
The Deploy page is where you launch a workload on your own fleet. HostSSH runs its own pipeline end to end: HostPack build → Docker run → Traefik route. There is no Nix and no third-party PaaS — the agent on your Node does the building, running, and routing.
You reach it from the sidebar. It needs the platform.deploy privilege; without it the
page shows a locked notice instead of the console.
What the page shows
At the top, four stat tiles: Deployments (total), Running, Databases, and Templates (how many one-click templates are in the catalog). A mode badge tells you which backend is actually executing:
| Badge | Meaning |
|---|---|
agent · live | A Node is connected — deploys are dispatched to it and really run. |
persisted · agent pending | Records are saved to the database, but no agent has claimed them yet (they stay queued). |
demo data · in-memory | Sample data only — no database, nothing is dispatched. |
Below that is the template catalog and, further down, a Deployments table listing everything you've launched (name, kind, state, domain, builder, host, created).
One-click templates vs. custom builds
The catalog splits into two kinds of source, and the difference is the builder:
- Prebuilt image (
builder: image) — databases and services ship as pinned OCI images that are pulled and run as-is. Templates include PostgreSQL (Postgres 16 with pgvector), Redis, MariaDB, MinIO, n8n, Uptime Kuma, Plausible Analytics, and Meilisearch. - Build from source (
builder: hostpack) — application frameworks are built from a git repo by HostPack, which autodetects the stack (no Dockerfile required). Templates include Next.js app, Node service, Static site, and Python service.
Search the catalog by name, category, or tag (for example postgres, n8n, next).
Click a card to open the deploy drawer pre-filled from that template; or click New
deployment to open the drawer in advanced mode for a fully custom build.
The deploy drawer
Quick mode asks only for the essentials:
| Field | What it does |
|---|---|
| Name | Lowercase letters, numbers and hyphens, starting with a letter. Used as the container name and subdomain slug. |
| Domain | Optional. When set, Traefik routes the app with automatic TLS. Leave blank to keep the app internal. |
For a template, any declared environment variables are preset for you, and secrets are auto-generated on the Node — the drawer tells you how many vars are preset.
Advanced options (toggle in the drawer footer, or open New deployment) expose the full spec:
| Field | What it does |
|---|---|
| Builder | HostPack (build from source) or Image (prebuilt OCI). |
| Port | The container port to route to (1–65535). |
| Readiness path | Optional HTTP path (e.g. /healthz) probed from the host after container liveness, before the deploy is called done. |
| Source | The repo/path (HostPack) or the image reference like postgres:16-alpine (image). |
| Network | Docker network name — defaults to hostssh. |
| Image limit (MB) | A Docker image-size budget. The default platform budget is 4096 MB; the drawer flags images above it. |
| Branch / tag | (HostPack only) which git ref to build — the default branch if left empty. |
| Migrate | A release-phase command run once before the app starts, e.g. prisma migrate deploy. |
| Verify | A drift gate run after migrate — a non-zero exit fails the deploy, blocking a behind schema. |
| Environment | Key/value pairs; mark any as secret. See Environment variables & secrets. |
Deploying validates the fields, creates the deployment record, dispatches it to the fleet (when a Node is connected), and takes you to the deployment detail page.
The dispatch is best-effort: only non-secret env values ride the job queue. Values marked generate are minted on the Node by the agent (their keys are listed, never their values); user-entered secret values are handed to the claiming agent separately, never stored in the queue.
The deployment detail page
Opening a deployment (from the table, or right after you deploy) shows three things.
Specification — the resolved spec: source, image, builder, port, network, host, resources, the image-budget verdict, the domain (a live link if set, otherwise "internal"), and when it was created.
Environment — the App Settings editor for this app's env vars and linked secret sources. Secret values are masked. Editing and saving here persists the desired settings; you redeploy to apply them. Full detail in Environment variables & secrets.
Logs — a live, honest log stream of the build→run→route progression, split into
build, deploy, and runtime channels. It streams over SSE and falls back to polling
if the connection drops; the header shows streaming · live, streaming · polling,
reconnecting…, or complete. While an agent hasn't claimed the deploy yet, it reads
"Waiting for an agent to claim this deployment…" — no fabricated output.
Actions
The header carries the lifecycle controls:
| Action | When it appears | What it does |
|---|---|---|
| Redeploy | Always | Re-queues the deploy so the build→run→route (and log stream) replays. |
| Cancel | While queued / building / deploying | Signals the agent to stop the in-flight run; the deployment flips to cancelled. |
| Stop | While running | Stops the container on the host; the deployment goes to stopped. |
| Cut over DNS | When a domain is set | Points the domain's A-record at the Node running the app. |
| Remove | Always | Type the app name to confirm; stops the container, tears down routing, and deletes the record. |
DNS cutover is a native, DNS-only flip: it resolves the target Node's public IP from the agent that ran the deployment, health-checks the target before moving DNS, updates the A-record grey-cloud (so the Node's Let's Encrypt cert keeps issuing), then re-checks the public domain and rolls back if that post-flip check fails. The app must be running, and the control plane needs a Cloudflare DNS token configured.
One-click managed database
The Provision database button (top of the page) queues a managed database on a Node
without going through the deploy drawer. Pick a name and an engine — PostgreSQL +
pgvector, Redis, or MariaDB. The agent creates it with a persistent volume,
generated credentials, and a DATABASE_URL in the on-host secrets store. An app deployed
separately can then pull those credentials by naming the database as a linked secret
source — see Environment variables & secrets.
Related
- Applications — the running-apps view, once a deploy is live.
- Environment variables & secrets — config, secrets, and linked databases.
- Provisioning — spin up (or connect) the Node your apps run on.
- Developer reference: Platform architecture, HostPack.