How deploys work
Connect a repo once. After that, you deploy by pushing to your main branch.
This page covers the GitHub flow. You point Dockhold at a repo, and every push to your main branch ships a new version. Here is what happens from your push to your app being live. No repo? You can also deploy a local folder with one command.
The flow
- You push to the main branch of a connected repo.
- GitHub notifies Dockhold that a push happened.
- Dockhold builds your app. Common stacks (Next.js, Vite, Node, static sites, FastAPI) build automatically on every account. Any other stack builds automatically with compute added, or from a Dockerfile at your repo root, which always works.
- The new version goes live with a rolling update. The old version keeps serving until the new one is healthy, so there is no downtime. The exception is an app with app storage: it is stopped first and then started again, so it is unreachable for a few seconds.
Your app stays at the same URL across deploys:
https://your-app.dockhold.app. You can watch each build happen
live in your dashboard.
Your first deploy
- Push to GitHub. Put your app in a GitHub repository.
- Connect the repo. Open the dashboard and connect it. On a paid plan Dockhold auto-detects the stack (Node, Python, Go, Rust, Ruby, Deno, Bun, Java, PHP) and builds it. On a free account, add a Dockerfile at the repo root and it builds from that.
- Go live. Your app comes up at its
dockhold.appURL.
One rule matters above all: your app must listen on 0.0.0.0
and read its port from the PORT environment variable. Don't
hardcode a port and don't bind to localhost, or traffic
can't reach it. See the Quickstart for examples.
Auto-deploy on push
How auto-deploy gets set up depends on how you connected the repo:
- Connected with the GitHub app: auto-deploy is on from the start. Every push to your main branch deploys. Nothing else to do.
-
Using a personal access token instead: turn on
auto-deploy in your app's GitOps settings, then add the push webhook it
shows you to your repo on GitHub (Settings, then Webhooks). Pick the
application/jsoncontent type and the push event.
Either way, Dockhold verifies every push notification cryptographically before it builds, so no one can trigger a deploy to your app without your secret. You can rotate that secret anytime from GitOps settings. Update it on GitHub at the same time so the two stay in sync.
Build again
Every app page has a Build again button next to Restart. It builds the app
again from the latest commit on your repository's default branch, or from
your last upload with the CLI, and puts it live when the build passes. If
the new build fails, the version that was live stays online. Use it after a
failed build, or to ship new commits when auto-deploy is off. Your AI tool
can do the same with the MCP rebuild_app tool.
Custom builds
Add a Dockerfile at the repo root and Dockhold uses it as-is. It
never overrides your Dockerfile. To point at one in a
subfolder, add a dockhold.json at the root:
{ "build": { "dockerfile": "path/to/Dockerfile" } }
No Dockerfile? Common stacks (Next.js, Vite,
Node, static sites, FastAPI) build automatically on every account. Automatic
builds for any other stack are included once you add compute. New to this?
Dockerfiles and dockhold.json explains
what these files are and has copy-paste templates for Node, Python and Go.
PHP apps
A repo with composer.json or index.php at the root
builds automatically once you add compute. On a free account, add a
Dockerfile.
-
Version. Dockhold picks the newest PHP your
require.phpincomposer.jsonallows, from 8.2, 8.3, 8.4 and 8.5, always on a release with the latest security fixes."php": "^8.3"builds 8.5,"php": "~8.3.0"stays on 8.3, and norequire.phpbuilds 8.5. Every rebuild picks up the newest patch release of that version. -
Older PHP. If
require.phpallows only PHP versions that no longer get security fixes, the build stops and tells you what to change: raise it, for example to^8.4, or add your own Dockerfile. -
What the build runs.
composer install --no-devwith an optimized autoloader, afternpm run buildwhenpackage.jsonhas a build script (Laravel with Vite). Whenpublic/index.phpexists,public/is the web root; otherwise the project root is, with dotfiles,vendor/andcomposer.jsonkept private. A request that matches no file goes toindex.php..htaccessfiles are not read. -
Extensions. The standard set plus
pdo_pgsql,pdo_mysql,bcmathand OPcache. Need another? Add a Dockerfile that starts from the official PHP image. -
Laravel. Add
APP_KEY(runphp artisan key:generate --showon your machine) andAPP_URL=$(DOCKHOLD_APP_URL)as variables. Logs go to your app's logs, sessions to a signed cookie and the cache to local files, until you setLOG_CHANNEL,SESSION_DRIVERorCACHE_STOREyourself. For a Managed Postgres database, setDB_CONNECTION=pgsqlandDB_URL=$(DATABASE_URL).
Next: write your first Dockerfile, connect a private repo, or set up environment variables and secrets.