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:

BadgeMeaning
agent · liveA Node is connected — deploys are dispatched to it and really run.
persisted · agent pendingRecords are saved to the database, but no agent has claimed them yet (they stay queued).
demo data · in-memorySample 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:

FieldWhat it does
NameLowercase letters, numbers and hyphens, starting with a letter. Used as the container name and subdomain slug.
DomainOptional. 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:

FieldWhat it does
BuilderHostPack (build from source) or Image (prebuilt OCI).
PortThe container port to route to (1–65535).
Readiness pathOptional HTTP path (e.g. /healthz) probed from the host after container liveness, before the deploy is called done.
SourceThe repo/path (HostPack) or the image reference like postgres:16-alpine (image).
NetworkDocker 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.
MigrateA release-phase command run once before the app starts, e.g. prisma migrate deploy.
VerifyA drift gate run after migrate — a non-zero exit fails the deploy, blocking a behind schema.
EnvironmentKey/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:

ActionWhen it appearsWhat it does
RedeployAlwaysRe-queues the deploy so the build→run→route (and log stream) replays.
CancelWhile queued / building / deployingSignals the agent to stop the in-flight run; the deployment flips to cancelled.
StopWhile runningStops the container on the host; the deployment goes to stopped.
Cut over DNSWhen a domain is setPoints the domain's A-record at the Node running the app.
RemoveAlwaysType 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.