# Dockhold > Dockhold puts an app online at a live, always-on, secure HTTPS URL. Two paths: connect a GitHub repo and push to your main branch, or run `npx dockhold deploy` in a local folder (no GitHub, no git). No servers to manage. Works for apps you wrote and apps your AI tool generated. > Need everything in one fetch, with the full text of every recipe inline? See https://dockhold.eu/llms-full.txt ## Deploy steps (GitHub) 1. Push your app to a GitHub repository. 2. Make the app listen on `0.0.0.0` and read its port from the `PORT` environment variable. Do not hardcode a port or bind to `localhost`. 3. Connect the repo at https://app.dockhold.eu. Build source order, first match wins: a `Dockerfile` at the repo root; a root `dockhold.json` with `{"build":{"dockerfile":"path/to/Dockerfile"}}`; on EVERY account, one of the stacks Dockhold recognises and builds from its own recipe: Next.js with `output: 'standalone'` in `next.config.js`, Vite (default `outDir`), Node with a `start` script and a `package-lock.json` and no `build` script, a static site with `index.html` at the root (only web file types are served: pages, styles, scripts, JSON, text, Markdown, CSV, calendar, XML, WebAssembly, PDF, 3D models, images, fonts, audio, video, plus every file in the root `.well-known/`; other dotfiles, symlinks, `package.json` and files such as `.env`, `.yml`, `.pem`, `.sql`, `.php` are left out, and folders are never listed; a `.php` file at the root makes it PHP instead), FastAPI with `requirements.txt` naming `fastapi` and `main.py` defining `app`; then, on any account with purchased compute, automatic detection of any stack (Node, Python, Go, Rust, Ruby, Deno, Bun, Java, PHP). PHP (purchased compute; `composer.json` or `index.php` at the root, checked before the other stacks) builds on the official PHP runtime with PHP-FPM, on the newest of 8.5, 8.4, 8.3, 8.2 that `require.php` allows, always a release with the latest security fixes; a `require.php` that allows only versions without security fixes is refused (raise it, for example to `^8.4`); Laravel needs `APP_KEY` and `APP_URL=$(DOCKHOLD_APP_URL)`; details: https://dockhold.eu/docs/concepts/how-deploys-work#php. A FREE account whose repo matches none of these fails within seconds with "Could not build this automatically." followed by what was seen and the one change that fixes it: apply the fix, or ship a `Dockerfile`. Recognised stacks use npm: commit `package-lock.json` (pnpm, Yarn and Bun lockfiles are declined with the fix named). Free image ceiling is 300 MB; the recognised stacks build at about 60 MB. 4. Read all configuration from environment variables. Set plain values in the dashboard, or with the MCP `set_app_variable` tool. Set secrets (API keys, tokens, passwords) under Secrets in the dashboard sidebar (https://app.dockhold.eu/settings/vault). The `set_app_secret` tool exists only over the remote HTTP MCP path with a dashboard token that has the `secrets` scope; the browser sign-in the `dockhold mcp` bridge uses never has it, so over the bridge, send the user to the dashboard. Never put a secret in a plain variable: `set_app_variable` refuses a secret-looking name. 5. Need a database? Enable the managed Postgres add-on; Dockhold injects `DATABASE_URL`. Do not run your own database. 6. Push to your main branch to deploy. The app goes live at `https://.dockhold.app`; later pushes redeploy automatically. To build the latest commit again without a new push (after a failed build, or when auto-deploy is off), press Build again on the app page, or call the MCP `rebuild_app` tool. ## Deploy steps (no GitHub: from a local folder) 1. Requires Node.js >= 18. From the project folder run `npx dockhold login` (opens a browser once to authorize; headless: `dockhold login --token` and paste an API token). 2. Run `npx dockhold deploy`. The CLI packs the folder (always excluding `.git`, `node_modules`, `.dockhold`, and every `.env*` file, plus `.gitignore` matches; a `.dockholdignore` file adds more and wins over `.gitignore`), uploads it (cap 500 MB), builds it (same order as a GitHub deploy: a `Dockerfile` at the folder root, then a root `dockhold.json` with `{"build":{"dockerfile":"path"}}`, then the stacks Dockhold recognises on every account, then automatic detection of any stack with purchased compute), and prints the live `https://.dockhold.app` URL. With no `Dockerfile` the CLI prints "No Dockerfile found. The build will detect your stack." and uploads; a stack Dockhold does not recognise on a FREE account is declined within seconds with "Could not build this automatically." and the fix. Free image ceiling is 300 MB, so use an `-alpine`/`-slim` base. 3. Flags: `--name ` sets the app name; `--env KEY=VALUE` (repeatable) sets variables; `--db` adds the managed database (app reads `DATABASE_URL`). 4. `.env` files are never uploaded. Set variables with `--env` or in the dashboard; secrets go under Secrets in the dashboard sidebar. 5. To ship a new version, run `npx dockhold deploy` again from the same folder. These apps have no git auto-deploy. Build again on the app page, or the MCP `rebuild_app` tool, builds the last upload again. Restart only restarts the version already running. 6. The `0.0.0.0:$PORT` rule applies exactly as in the GitHub path. ## Rules for generated apps - Bind to `0.0.0.0` and the `PORT` env var. Never `localhost` or a fixed port. - `PORT`, `DOCKHOLD_APP_URL` and `DOCKHOLD_APP_HOSTNAME` are set by Dockhold on every app, and `DATABASE_URL` when the managed database is on. Read them; never define or override them. `DOCKHOLD_APP_URL` is the app's own address with the scheme (`https://my-app-a1b2c3.dockhold.app`); `DOCKHOLD_APP_HOSTNAME` is the bare authority (`my-app-a1b2c3.dockhold.app`) for allowed-hosts / trusted-hosts settings, which reject a value containing `://`. The app cannot work its address out on its own: the container hostname is a runtime-assigned id. To move an app that already reads another name, do not edit code, set a dashboard variable to `$(DOCKHOLD_APP_URL)` or `$(DOCKHOLD_APP_HOSTNAME)` under the name it reads. - Apps are stateless by default: the filesystem is ephemeral and lost on restart. Persist data in the managed database, not on local disk. - If the app must keep files (a SQLite file, uploads, a tool with its own data directory), turn on app storage for that app (dashboard Size tab, or the MCP `set_app_storage` tool) and read the folder path from `DATA_DIR`. Files under `DATA_DIR` survive restarts and deploys; files outside it do not. Storage is opt-in, so never assume an app has it. An app with storage runs as a single copy, cannot have scheduled tasks, and is stopped and restarted on each deploy (a few seconds unreachable), so it must flush on SIGTERM within 60 seconds. - Pick the deploy path by where the code lives. In a GitHub repo: connect it in the dashboard, then push to the main branch. Only local (no remote): run `npx dockhold login` then `npx dockhold deploy` from the folder. These are the only two deploy paths. ## Plans and pricing - [Pricing](https://dockhold.eu/pricing): full breakdown (source of truth for current prices). - You buy capacity (compute, database memory, storage) and split it across as many apps as you want. There is no limit on the number of apps, only on the size of your pool. - Billing is fixed, not metered. The monthly total is set by the capacity the account has bought and changes only when the user changes it. There is no per-request, per-bandwidth, or per-build metering, and no overage line item. Apps do not sleep or scale to zero; the free allowance is always-on. - Free allowance (every account, no card): 256 MB of compute + 10 GB of storage. The free 10 GB covers a managed database OR one app's own storage, not both. Public apps only. - App size (compute): $5/month per 512 MB RAM + 0.5 vCPU. CPU rides with RAM. Pick a size per app: 256 MB / 512 MB / 1 GB / 2 GB / 4 GB. - Database memory: $10/month per 1 GB of dedicated database RAM. This is QUERY SPEED, not disk space. "2 GB of database memory" does not mean 2 GB of storage; disk is the separate Storage line below and costs 50x less per GB. - Storage: $0.20/month per 1 GB of disk (first 10 GB free). One pool, allocated in 10 GB steps (10 / 20 / 30 / 50 / 100 GB) and grow-only, to a managed database, to an app's own storage, or to both. The free 10 GB covers one of the two, not both. - Paid-only (any account that has bought at least one unit of compute): private apps, custom domains, running more than one instance of an app, and database import. The managed database is NOT one of them: every account gets one, free included. Paid accounts also get nightly managed-database backups (newest 7 kept, no extra charge); free accounts get none. - Enterprise (custom): capacity sized to you, priority support. Contact sales@dockhold.eu. - Compared with other hosts, feature by feature with a priced example and dated, linked sources: [Dockhold vs Railway](https://dockhold.eu/vs/railway), [Dockhold vs Render](https://dockhold.eu/vs/render), [Dockhold vs Fly.io](https://dockhold.eu/vs/fly). ## Account and platform - [Sign in / sign up](https://app.dockhold.eu/login): sign in with GitHub or Google; the free allowance needs no card. Deploying your own private repos needs GitHub connected. - [Dashboard](https://app.dockhold.eu): connect a repo, deploy, manage apps, set variables and secrets. - [Status](https://stats.uptimerobot.com/UekCQqu8we): live platform uptime. - [Security and trust](https://dockhold.eu/trust): EU hosting, isolation, encryption at rest and in transit, tamper-evident audit log, subprocessors, GDPR data ownership, and DPA (Article 28) on request. - [About](https://dockhold.eu/about): what Dockhold is, who it's for, and the principles behind it. The [founder story](https://dockhold.eu/about/founder) is on a subpage. - [Blog](https://dockhold.eu/blog): findings, deploy guides, and product notes ([RSS](https://dockhold.eu/rss.xml)). - [Terms, Privacy, Impressum](https://app.dockhold.eu/legal): legal pages. Hosted in the EU (Germany). ## Docs - [Quickstart](https://dockhold.eu/docs/quickstart): the shortest path from repo to live URL. - [Deploy from your computer](https://dockhold.eu/docs/concepts/deploy-from-your-computer): the CLI deploys a local folder with no GitHub; install, login, what gets uploaded, the 500 MB cap, updating. - [Agent rules](https://dockhold.eu/agent-rules): copy-paste rules for Cursor (.cursorrules), AGENTS.md, and CLAUDE.md. - [Docs home](https://dockhold.eu/docs): all guides. ## Core concepts - [How deploys work](https://dockhold.eu/docs/concepts/how-deploys-work): connect a repo, push to the main branch, Dockhold builds and ships with a zero-downtime rolling update. GitHub-app connection auto-deploys on push; a personal-access-token connection needs a push webhook added on GitHub. - [Add a Deploy button](https://dockhold.eu/docs/concepts/deploy-button): a README badge that deploys a repo on the visitor's own account. Markdown: `[![Deploy on Dockhold](https://dockhold.eu/button.svg)](https://app.dockhold.eu/new?repo=&name=&ref=button)`; HTML: `Deploy on Dockhold`. `repo` must be public, or the visitor's own connected repo; `name` prefills the app name (omit it and the name comes from the repo); `ref=button` is a plain attribution tag, never shown and sets nothing on the app. The visitor signs in, confirms the prefilled form, and the app builds like any repo (Dockerfile at the root, or auto-detection on a paid account). - [Dockerfiles and dockhold.json](https://dockhold.eu/docs/concepts/dockerfiles): what a Dockerfile is and copy-paste templates (Node/Python/Go); `dockhold.json` is optional and only needed to point at a Dockerfile outside the repo root (`build.dockerfile`, path relative to root, must exist or the deploy fails). Not required for the stacks Dockhold recognises on every account (Next.js standalone, Vite, Node with a start script, static, FastAPI); automatic detection of any other stack needs purchased compute; a Dockerfile always works. - [Connecting GitHub](https://dockhold.eu/docs/concepts/connect-github): public repos need no setup. Private repos use either the Dockhold GitHub app (short-lived, read-only, per-repo access) or a personal access token stored as a secret named `GITHUB_TOKEN`. - [Environment variables and secrets](https://dockhold.eu/docs/concepts/environment-variables): plain config goes in dashboard variables; secrets go under Secrets in the dashboard sidebar (encrypted, connected only to the apps you choose, injected as env vars). A build cannot read dashboard variables, and a browser bundle is public, so a front-end SPA reads runtime config from `window.__APP_CONFIG__`. Dockhold also sets variables you cannot override: `PORT`, `DOCKHOLD_APP_URL` (own address with scheme) and `DOCKHOLD_APP_HOSTNAME` (bare authority, for allowed-hosts settings), plus `DATABASE_URL` and `DATA_DIR` when those add-ons are on. An app migrating from another host keeps its own variable name and sets the value to `$(DOCKHOLD_APP_URL)`. - [Databases and storage](https://dockhold.eu/docs/concepts/databases): the filesystem is ephemeral; persist data in the managed database. Enable the add-on and read `DATABASE_URL` (also `VECTOR_STORE_URL`). Vector search is built in: store a `vector(N)` column and search with the `<->` operator. Paid accounts: nightly backups of the managed database, newest 7 kept, encrypted per app, stored in a separate location in Germany, locked 7 days. Restore on request: tell the user to write to support@dockhold.eu with the app and date; it is restored into a second database beside the live one (same user and password), live data untouched. Free accounts: no backups. - [App storage](https://dockhold.eu/docs/concepts/app-storage): opt-in persistent storage for one app. Off by default. Turn it on from the app's Size tab and the app reads the folder path from `DATA_DIR` (`/data`); everything under it survives restarts and deploys, everything outside it is scratch space. Sizes 10 / 20 / 30 / 50 / 100 GB from the same storage pool as the managed database, grow-only, and the free allowance covers a managed database OR app storage, never both. While storage is on the app runs as a single copy and cannot have scheduled tasks (both refused in both directions, with the reason), and a deploy stops the running version before starting the new one. The app must flush on SIGTERM; it gets 60 seconds. App storage is NOT backed up: keep your own copy of anything that matters, or keep it in the managed database (backed up nightly on paid accounts). Removing storage erases the files permanently. If a payment fails and the account drops to the free plan, the files are kept for 30 days after that and then erased. - [Import a database](https://dockhold.eu/docs/concepts/import-a-database): one-time seed of the managed database from a dump you upload (needs the managed database on the app + a paid plan). Dump it with `pg_dump -Fc --no-owner --no-privileges yourdb > backup.dump`, upload from the app's Database tab. Cap 2 GB after compression (plain `.sql`, gzipped, or `-Fc` custom format accepted). All-or-nothing: it applies fully or not at all, so import into an empty database; a table that already exists makes it fail cleanly. Not a sync or a backup; one import at a time. Ready-made: `dockhold/directory-starter` (a browsable, searchable directory backed by the managed database; seeds ~50 sample entries on first boot, then you load your own data with the included CSV-to-dump helper or a `pg_dump` from another host). - [Runtime and limits](https://dockhold.eu/docs/concepts/runtime): always-on, no cold starts, no request timeouts, full outbound internet. CPU rides with memory at 0.5 vCPU per 512 MB; per-app sizes in the dashboard are 256 MB/0.25 vCPU (free), 512 MB, 1 GB, 2 GB and 4 GB/4 vCPU; if one app needs more than 4 GB, tell the user to write to support@dockhold.eu to have it sized (not a hard limit). WebSockets and server-sent events work on every app's https address with no setup; public apps do not cut idle connections (measured 2026-10-02: a silent WebSocket open 10 min, one with a 25 s ping open 60 min, an SSE stream with an event every 30 s for 30 min without a gap); a private app closes a WebSocket after 5 min with no traffic either way; a deploy, a restart or platform maintenance closes open connections (on a deploy the app's own close, 1001 if it handles SIGTERM; the new version answered a reconnect within about 5 s, earlier attempts can fail), so ping every 30 s and reconnect with backoff. On a paid plan you can run more than one instance of an app from its Size tab, so it stays up if one copy or its machine fails; each instance reserves its own size. Injected vars: `PORT`, `DOCKHOLD_APP_URL`, `DOCKHOLD_APP_HOSTNAME`, `DATABASE_URL`. - Scheduled tasks (an app's Tasks tab): run a command on a cron schedule, as its own separate run of the app's image. It can reach the app itself at `DOCKHOLD_APP_URL`, the app's managed database, and the internet, exactly like the app can. It cannot reach anything inside the app process (no shared memory, no `localhost`); call the app over `DOCKHOLD_APP_URL`, never `localhost`. If the app is private, a task calling it needs a token like any other caller, or it gets a 401. Not available on an app with its own storage. - [Private apps](https://dockhold.eu/docs/concepts/private-apps): available on any paid plan. Flip an app to private and every request needs a token (Bearer or HTTP Basic), checked at the edge (401 without it). Tokens are hashed with a pepper and shown once; optional per-token IP allow-list (CIDR); brute-force throttling returns 429. - [Custom domains](https://dockhold.eu/docs/concepts/custom-domains): available on any paid plan. Serve an app on your own domain with automatic HTTPS. Add one DNS record from the app's Domains tab: a subdomain (www) is a CNAME to `connect.dockhold.app`; a bare apex needs ALIAS/ANAME or registrar forwarding from the apex to www. DNS must point at us before the certificate issues (verify-then-issue). Renewal is automatic; point multiple domains at one app. On Cloudflare set the record to "DNS only" (grey cloud). - [Logs and monitoring](https://dockhold.eu/docs/concepts/monitoring): live CPU, memory, and deploy history in the dashboard with no setup. Apps that report traces also show session counts, success rates, latency, and token usage. ## Templates (ready-made starter repos) - [Templates](https://dockhold.eu/templates): every ready-made starter repo under `github.com/dockhold`, grouped by what it builds, each with its recipe and a one-click Deploy link. Each card says whether it runs on the free plan. - [templates.json](https://dockhold.eu/templates.json): the same catalog as JSON, built with the page. `templates[]` holds `slug`, `title`, `body`, `category`, `categoryLabel`, `plan` (`free` or `paid`), `addons`, `tags`, `recipe`, `source` (the GitHub repo), `deploy` (opens the deploy form with the repo filled in) and `addedOn`; `groups[]` names the categories. Fetch it to pick a starter instead of guessing a repo name. ## Recipes (self-contained per-stack deploy guides) - [Deploy a Vite + React app](https://dockhold.eu/docs/recipes/deploy-a-vite-react-app): recognised on every account (build, then serve `dist/` on `$PORT`); the Dockerfile in the recipe pins the same build with `serve -s dist -l $PORT`. - [Deploy a Next.js app](https://dockhold.eu/docs/recipes/deploy-a-nextjs-app): full-stack Next.js; start with `next start -H 0.0.0.0 -p $PORT`. - [Deploy a static site](https://dockhold.eu/docs/recipes/deploy-a-static-site): plain HTML/CSS/JS; `index.html` at the root builds automatically and serves web file types only. - [Deploy a landing page](https://dockhold.eu/docs/recipes/deploy-a-landing-page): a one-page product landing (hero, benefits, how it works, FAQ with `
`, sign-up or contact call to action, footer with legal links) as plain HTML and CSS, no JavaScript; the call to action is a `mailto:` by default, or a form whose `action` the user points at their own form endpoint; `index.html` at the root builds automatically on every account. Free plan, App size 256 MB, no variables, no secrets. Ready-made: `dockhold/landing-page-starter`. - [Deploy a Node / Express API](https://dockhold.eu/docs/recipes/deploy-a-node-express-api): `app.listen(process.env.PORT, "0.0.0.0")`. - [Deploy a Python / FastAPI app](https://dockhold.eu/docs/recipes/deploy-a-python-fastapi-app): recognised on every account (`requirements.txt` naming `fastapi` + root `main.py` defining `app`, started with `uvicorn main:app` on `$PORT`); the recipe's minimal Dockerfile pins the same. - [Deploy a Lovable export](https://dockhold.eu/docs/recipes/deploy-a-lovable-export): Lovable exports a Vite + React app; add the Vite Dockerfile (build + serve `dist/` on `$PORT`) and wire the Supabase URL + anon key from dashboard variables read at runtime (`window.__APP_CONFIG__`), not a committed `.env.production`. - [Deploy a Bolt.new export](https://dockhold.eu/docs/recipes/deploy-a-bolt-new-export): identify the stack (usually Vite); a Vite SPA is recognised and built on every account; the Vite Dockerfile (build + serve `dist/`) pins it. - [Connect a Postgres database](https://dockhold.eu/docs/recipes/connect-a-postgres-database): enable the managed database; Dockhold injects `DATABASE_URL` (and `VECTOR_STORE_URL`, with pgvector built in). Read it; never define it. Ready-made: `dockhold/postgres-starter` (CRUD API). - [Deploy a vector database (pgvector)](https://dockhold.eu/docs/recipes/deploy-a-vector-database): the managed database ships with pgvector enabled. Store embeddings in a `vector(N)` column and search with the `<->` operator. Ready-made: `dockhold/pgvector-starter` (semantic search). - [Deploy a directory site](https://dockhold.eu/docs/recipes/deploy-a-directory-site): a browsable, searchable directory (`/` with `?q=` search and `?category=` filter, `/e/` per entry, `/api/entries` JSON) on the managed database; tick Add a managed database on the deploy form, `DATABASE_URL` is injected, free plan. Seeds about 50 sample entries on first boot, only when the table is empty. Own data: CSV with header `name,tagline,description,url,category,pricing,tags` (only `name` required, `tags` pipe-separated), `node scripts/csv-to-dump.js data.csv > my-directory.sql` in a copy of the template (creates the table if missing, upserts on `slug`; prepend `TRUNCATE entries;` to drop the samples), upload on the app's Data tab under Import an existing database. Import needs a paid plan and succeeds once per database. Read-only to visitors. Ready-made: `dockhold/directory-starter`. - [Deploy a full-stack app (React + API + Postgres)](https://dockhold.eu/docs/recipes/deploy-a-full-stack-app): two apps (just needs enough compute in your pool), a React frontend + a separate Express + Postgres API. Deploy the API first (with the managed database), then set the web app's `API_URL` dashboard variable to the API URL and restart (read at runtime, no rebuild), and set `ALLOWED_ORIGIN` on the API to the web URL for CORS. - [Deploy connected services](https://dockhold.eu/docs/recipes/deploy-connected-services): deploy several web services that call each other in one step. Set it up in the dashboard (New App → "Deploy them together"): add each service (a GitHub repo URL + port) and, per environment variable, "Link a service" so it resolves to that sibling's live `https://.dockhold.app` URL. Dockhold builds all services in parallel and injects each one's URL into the services that linked to it once it's live; a dependent may restart once when its target comes online, and until then the linked variable is simply absent. Your app reads the variable name you chose (e.g. `API_URL`); never hardcode a sibling's URL. First cut: web services only, one repo per service, no managed database inside a connected app yet (deploy a database-backed service on its own and link to it by URL). There is no push-time or MCP path yet; connected apps are created in the dashboard. - [Deploy an MCP server](https://dockhold.eu/docs/recipes/deploy-an-mcp-server): a remote MCP server over the Streamable HTTP transport, served on `0.0.0.0:$PORT` at `POST /mcp` (official `@modelcontextprotocol/sdk`, stateless). Any MCP client connects to the live URL. Build: a Node project with a `start` script and a `package-lock.json` is recognised on every account (`npm ci --omit=dev`, `npm start`); to pin it, ship a `Dockerfile` (`FROM node:22-alpine`, `npm ci --omit=dev`, `CMD ["node", "server.js"]`). Python/FastMCP: not a recognised stack, so ship a Dockerfile or use purchased compute; `mcp.run(transport="http", host="0.0.0.0", port=int(os.environ["PORT"]))` (older releases name the transport `streamable-http`); overview at [Host a FastMCP server](https://dockhold.eu/blog/host-fastmcp-server). - [Deploy from your AI tool (MCP)](https://dockhold.eu/docs/recipes/deploy-from-your-ai-tool): let an AI coding tool (Claude Code, Cursor, VS Code, Codex, Gemini CLI, Windsurf, any MCP client) deploy and manage apps on Dockhold. Install: every client starts the stdio bridge `npx -y dockhold mcp` (Node 18+). Claude Code: `/plugin marketplace add dockhold/mcp` then `/plugin install dockhold@dockhold`. VS Code: `code --add-mcp '{"name":"dockhold","command":"npx","args":["-y","dockhold","mcp"],"env":{"DOCKHOLD_REF":"vscode"}}'`. Cursor (`.cursor/mcp.json`), Windsurf (`~/.codeium/windsurf/mcp_config.json`), any JSON-config client: `{"mcpServers":{"dockhold":{"command":"npx","args":["-y","dockhold","mcp"],"env":{"DOCKHOLD_REF":""}}}}`. Codex: `codex mcp add dockhold --env DOCKHOLD_REF=codex -- npx -y dockhold mcp`. Gemini CLI: `gemini mcp add -e DOCKHOLD_REF=gemini dockhold npx -y dockhold mcp`. Sign in once from any terminal with `npx dockhold login` (opens a browser; the saved sign-in serves both the bridge and `npx dockhold deploy`; no token goes into any client config). Before sign-in, every tool call returns `Not signed in to Dockhold. Run "npx dockhold login" in a terminal, then try again.`: relay or run that command, then retry; the bridge picks the sign-in up without a client restart. If the client times out while `npx` downloads the package on first start: `npm i -g dockhold`, then command `dockhold`, args `["mcp"]`. Tools: list apps and connected repos, read status/logs/resource usage, deploy a public or private repo (private: `list_github_repos` for the `installation_id`, then `deploy_app` with `github_installation_id`), sized with `memory_mb` for either path (omit it for the minimum slice, 256 MB, which always fits if the pool has any room left, or pass it, checking `get_resource_usage` first to fit the remaining pool; the response reports the size given and what's left), restart (`redeploy_app`), build again from the latest commit or upload (`rebuild_app`), set and unset variables, resize an app or its managed database (`resize_app`/`resize_database`/`resize_database_storage`; check `get_resource_usage` for valid sizes and free pool), add, grow or remove an app's persistent storage (`set_app_storage`/`remove_app_storage`), deploy several connected services (`deploy_group`). The bridge sign-in carries the deploy scope only: the secrets tools (`set_app_secret`, `bind_app_secret`, `unset_app_secret`, `list_app_secrets`, `list_secrets`) are refused on it, so set secrets in the dashboard under Secrets in the sidebar, and `set_app_variable` refuses a secret-looking name. Remote HTTP path, only for a client that cannot start a command: server `https://api.dockhold.eu/mcp`, authenticated with a dashboard API token (API tokens in the dashboard sidebar; `dh_mcp_` prefix, shown once; read-only or read+deploy, plus an additive `secrets` checkbox) as `Authorization: Bearer`. With `secrets` the secrets tools work: write-only (no tool ever returns a stored value, only the dashboard does, while signed in); the token writes to every app on the account, not only the one you name; a value you pass through `set_app_secret` has passed through your own context, so read it from a local file (such as `.env`) rather than asking the person to type it into the chat, and tell them to rotate anything they did paste in. - [Deploy n8n](https://dockhold.eu/docs/recipes/deploy-n8n): self-host your own n8n (give it a larger app size, e.g. 1 GB, plus database memory), backed by the managed database (set `N8N_ENCRYPTION_KEY` as a secret; the template maps `DATABASE_URL` to Postgres). n8n is under the Sustainable Use License: for your own use, not reselling as a service. Ready-made: `dockhold/n8n-starter`. - [Deploy PocketBase](https://dockhold.eu/docs/recipes/deploy-pocketbase): a backend in one binary (database, auth, file storage, admin panel). Deploy at App size 256 MB with App storage on at 10 GB, the free plan covers both. Add two secrets, `PB_ADMIN_EMAIL` and `PB_ADMIN_PASSWORD` (8 to 71 characters), under entry names that belong to this app, because secrets are shared by name and a reused entry would share a password across apps. The admin account is created from those secrets before PocketBase starts listening. MIT licensed. Ready-made: `dockhold/pocketbase-starter`. - [Deploy Open WebUI](https://dockhold.eu/docs/recipes/deploy-open-webui): a chat interface for language models with document upload, search and an admin panel. Needs a paid plan: deploy at App size 2 GB (grow it as usage grows) with App storage on at 10 GB. Add three secrets under entry names that belong to this app (for example `open-webui-admin-email`, `open-webui-admin-password`, `open-webui-openai-key`), mapped to `WEBUI_ADMIN_EMAIL`, `WEBUI_ADMIN_PASSWORD` (8 to 72 characters) and `OPENAI_API_KEY`; add a plain `OPENAI_API_BASE_URL` to point at Anthropic (`https://api.anthropic.com/v1`, Claude models), OpenRouter (`https://openrouter.ai/api/v1`) or another OpenAI-compatible provider instead of OpenAI. No local chat models, no Ollama. Open WebUI License (BSD-3 plus a branding clause past fifty users in a rolling thirty days). Ready-made: `dockhold/open-webui-starter`. - [Deploy MCP Memory Service](https://dockhold.eu/docs/recipes/deploy-mcp-memory-service): one long-term memory for claude.ai, ChatGPT, Claude Code and any MCP client, as a remote MCP server (Streamable HTTP at `/mcp`) with OAuth login. Deploy at App size 256 MB with App storage on at 10 GB; the free plan covers both when the account has no managed database. On the deploy form add one secret under an entry name that belongs to this app (for example `mcp-memory-api-key`), mapped to `MCP_API_KEY`, 32 or more random characters (`openssl rand -hex 32`); the app refuses to start without it. `GET /health` returns `{"status":"ok"}`. Clients log in once on the app's OAuth page with the API key, or send it as `Authorization: Bearer ` or `X-API-Key`. Claude Code: `claude mcp add --transport http memory https:///mcp`, then `/mcp` to log in. Changing the key and restarting signs every client out; the memories stay. MCP Memory Service is Apache-2.0, the template glue MIT. Ready-made: `dockhold/mcp-memory-service-starter`. - [Deploy a Telegram bot](https://dockhold.eu/docs/recipes/deploy-a-telegram-bot): an always-on bot (Telegraf, long polling) plus a small health server on `0.0.0.0:$PORT`. Set `BOT_TOKEN` (from BotFather) as a secret; run a single instance. Ready-made: `dockhold/telegram-bot-starter`. - [Deploy a Discord bot](https://dockhold.eu/docs/recipes/deploy-a-discord-bot): an always-on slash-command bot (`discord.js`, `/ping` and `/roll`) plus a status server on `0.0.0.0:$PORT`. Free plan, App size 256 MB. On the deploy form add one secret under an entry name that belongs to this app (for example `discord-bot-token`), mapped to `DISCORD_TOKEN` (Discord developer portal, Bot page, Reset Token). Optional variable `DISCORD_GUILD_ID` registers commands to one server at once instead of globally. Run one copy only; two copies both answer every command. `GET /` returns `gateway` (`connected`, `connecting` or `no token`), `message` and the bot's `invite` link; a missing or refused token is reported there and the app does not restart-loop. Ready-made: `dockhold/discord-bot-starter`. - [Host a Godot or Unity web build](https://dockhold.eu/docs/recipes/host-a-godot-or-unity-web-build): serve a Godot 4 or Unity web export with the headers it needs (COOP `same-origin` and COEP `require-corp` for threaded Godot; `Content-Encoding` for Unity `.br` and `.gz`). Caddy, with the game compressed once at image build. Free plan, App size 256 MB, no secrets. Two files, `Dockerfile` and `Caddyfile`, go in the export folder next to `index.html` (the entry page must be named `index.html`); deploy from GitHub or run `npx dockhold login` then `npx dockhold deploy` in that folder. Optional variable `CROSS_ORIGIN_ISOLATION=off` drops the two isolation headers (for pages that embed third-party frames). Unity: Compression Format Brotli or Gzip, Decompression Fallback off. Paid plan options: a private playtest link (app's Access tab) and a custom domain (Domains tab). Ready-made: `dockhold/game-web-starter`. - [Host a Discord Activity](https://dockhold.eu/docs/recipes/host-a-discord-activity): Trails, a realtime light-cycle arena (up to 8 riders, last one riding wins, everyone else watches, wins scoreboard, who-is-here list) that runs inside a Discord voice channel; one app serves the page, the Discord sign-in and the WebSocket at `/ws`. Free plan, default App size. Needs a Discord application. Plain variable `DISCORD_CLIENT_ID` on the deploy form; secret under an entry name that belongs to this app (for example `discord-activity-client-secret`) mapped to `DISCORD_CLIENT_SECRET`. Redirects needs `https://127.0.0.1`; enable Activities; URL Mappings root `/` targets the app host without `https://`. Run one copy; a restart or deploy ends the round and everyone lands in a fresh room. Until Discord verifies the application only the developer, the developer team and up to 50 App Testers can launch it, in servers with fewer than 25 members. Ready-made: `dockhold/discord-activity-starter`. - [Run a coding agent 24/7](https://dockhold.eu/docs/recipes/run-a-coding-agent): Claude Code, unmodified, headless, with the user's own Anthropic API key; it works GitHub issues that carry a label in one repository and opens a pull request per issue. Needs a paid plan: App size 1 GB, App storage on at 10 GB; pick Private under Visibility (the status page shows repository and issue titles; nothing needs to reach the app). Secrets under entry names that belong to this app (for example `coding-agent-anthropic-key`, `coding-agent-github-token`), mapped to `ANTHROPIC_API_KEY` and `GITHUB_TOKEN` (fine-grained, one repository, Contents, Issues and Pull requests read and write); optional `DOCKHOLD_API_TOKEN` (Read & deploy or Read-only) gives the agent the Dockhold MCP server. Plain variable `AGENT_REPO` (`owner/name`); optional `AGENT_LABEL` (`agent`), `AGENT_POLL_MINUTES` (10), `AGENT_MODEL` (`sonnet`), `AGENT_ALLOWED_TOOLS`, `AGENT_MAX_BUDGET_USD` (2), `AGENT_TASK_MINUTES` (60). Only issues by authors with write access run; the agent never sees the GitHub token; the user pays Anthropic directly; use an API key, not a subscription. Ready-made: `dockhold/coding-agent-starter`. - [Test a branch before you merge](https://dockhold.eu/docs/recipes/test-a-branch-before-you-merge): give a branch its own app with the CLI. In a git worktree of the branch (`git worktree add ../- `), run `npx dockhold deploy --name - --env KEY=VALUE` (signed in with `npx dockhold login`). The first deploy in a folder creates a new app and records it in that folder's `.dockhold/app.json`, so the main folder's app is untouched. Needs compute added in Usage & billing (the free plan runs one app). The app does not follow commits: rerun `npx dockhold deploy` in the worktree after each change (uploads the folder as it is on disk, same app, same URL). `--name` and `--env` count only on the first deploy; later variable changes go on the app's Variables tab, then Restart. `.env` files never upload. The branch app shares no variables, secrets or database with the main app (`--db` gives it its own empty one). Never commit `.dockhold/`: a committed `app.json` makes a worktree deploy replace the main app. After merging, the user deletes the app (app's Settings tab, Delete this app) and runs `git worktree remove`. - [Run a Hermes coding agent](https://dockhold.eu/docs/recipes/run-hermes-coding-agent): Hermes Agent by Nous Research, unmodified, headless, with the user's own model key from any API-key provider Hermes supports (OpenRouter by default; `custom` plus an `https://` `AGENT_BASE_URL` for OpenAI or any OpenAI-compatible endpoint); it works GitHub issues that carry a label in one repository and opens a pull request per issue. Needs a paid plan: App size 1 GB, App storage on at 10 GB; pick Private under Visibility. Secrets under entry names that belong to this app (for example `hermes-coding-agent-model-key`, `hermes-coding-agent-github-token`), mapped to `AGENT_API_KEY` and `GITHUB_TOKEN` (fine-grained, one repository, Contents, Issues and Pull requests read and write); optional `DOCKHOLD_API_TOKEN` (Read & deploy or Read-only) gives the agent the Dockhold MCP server. Plain variables `AGENT_REPO` (`owner/name`) and `AGENT_MODEL` (as the provider names it); optional `AGENT_PROVIDER` (`openrouter`), `AGENT_BASE_URL`, `AGENT_LABEL` (`agent`), `AGENT_POLL_MINUTES` (10), `AGENT_TOOLSETS` (`file,terminal,todo`), `AGENT_MAX_TURNS` (90), `AGENT_TASK_MINUTES` (60). Only issues by authors with write access run; the agent never sees the GitHub token; only a root `AGENTS.md` is loaded from the checkout; pull requests show the model and tokens used; the user pays the provider directly. Ready-made: `dockhold/hermes-coding-agent-starter`. - [Run a Hermes chat agent](https://dockhold.eu/docs/recipes/run-hermes-chat-agent): Hermes Agent by Nous Research, unmodified, always on, answering in Telegram (Discord, Slack and other chat apps from its setup page), with config, chat history, skills and memory on App storage. Needs a paid plan: App size 1 GB, App storage on at 10 GB. One secret under an entry name that belongs to this app (for example `hermes-agent-admin-password`, at least 12 characters) mapped to `ADMIN_PASSWORD`. Open the app's URL and sign in as `admin`; the setup page picks the model provider, its API key, the model and the chat channels. The user brings their own model key; Dockhold never pays for or resells model usage. A new person who messages the bot gets a pairing code; approve it under Users in the admin panel. Ready-made: `dockhold/hermes-agent-starter`. ## Troubleshooting (common first-deploy failures) - [Hardcoded localhost URL](https://dockhold.eu/docs/troubleshooting/hardcoded-localhost-url): bind servers to `0.0.0.0:$PORT` and read external URLs from env vars; `localhost` points nowhere in production. - [Missing environment variables](https://dockhold.eu/docs/troubleshooting/missing-environment-variables): `.env` isn't deployed. Set config in the dashboard, secrets under Secrets in the dashboard sidebar. The build can't see dashboard variables, so a Vite SPA reads browser config at runtime (`window.__APP_CONFIG__`) and Next.js `NEXT_PUBLIC_` values are set in code; never put a secret behind a public prefix. - [CORS error after deploying](https://dockhold.eu/docs/troubleshooting/cors-error-after-deploying): allow the frontend's deployed origin via an env var (don't use `*`), or serve frontend + API from one origin and use relative paths.