Hatchik help → How apps run on Hatchik
How apps run on Hatchik
This page is for the AI building the app. It is the contract an app has to meet to run on Hatchik. Meeting it is the difference between a publish that works first time and one that fails a health check.
The contract
- **A
Dockerfileat the root of the files.** Hatchik builds the app from it. Everything else in the file list is the app. Without a root Dockerfile the publish is rejected immediately. - **Listen on the port in the
PORTenvironment variable.** Hatchik sets it to 8000. Read it; do not hard-code it. - **Bind
0.0.0.0, never127.0.0.1orlocalhost.** An app bound to localhost is unreachable from outside its container and every publish will fail the health check. - **Serve
GET /healthzreturning HTTP 200.** Hatchik polls it after every publish to decide whether the app came up. No/healthz, no successful publish. - **Read
DATABASE_URLfrom the environment.** Hatchik injects it, in the shapepostgresql://user:pass@postgres:5432/db. Never hard-code it, never print it, and never write code whose purpose is to discover or dump it. - Run migrations and seeding at startup. The host
postgresresolves only inside the app's own network. Nothing outside — not the customer's machine, not a script you run locally — can reach the database, so boot-time is the only place schema work can happen. - Expect the database to be empty on the first boot and populated on every boot after. Migrations must be safe to run repeatedly.
- **Save files in
/data, and nowhere else.** Publishing builds the app again from the files you send, so the app's own filesystem is replaced every single time./datais a folder that is kept. A photo somebody uploaded, written to./uploads; a generated PDF; a SQLite file sitting beside the code — all gone, permanently, at the next publish, with nothing to restore them from. Write everything the app stores under/data, and create the sub-folders you want there at startup. **Records belong in Postgres; files belong in/data.**
Static sites need none of this. A website is files; publish index.html and the rest and they are served.
What Hatchik guarantees back
- A separate Postgres database for the draft and for the live site. They never share data.
DATABASE_URLandPORTinjected into both.- If the customer has added an Anthropic or OpenAI key on the project page,
ANTHROPIC_API_KEY/OPENAI_API_KEYare injected too. Runtime AI always runs on the customer's own key — Hatchik does not sell AI credit. Never ask the customer for a key in chat. - A rebuild of the app on every publish. Two things are left untouched by it: the Postgres database, and the
/datafolder. Nothing else inside the app survives a publish — that is the whole of the rule, and there is no way to keep anything else, because the new app is built from scratch and replaying an old copy's files onto it would resurrect what the new version deleted. - HTTPS on
<name>.hatchik.com, ondev.<name>.hatchik.com, and on any custom domain, issued and renewed automatically. - A lock in front of the draft.
dev.<name>.hatchik.comanswers a "this draft is private" page with a 401 to anything that does not carry the project's draft key, and so does a custom domain pointed at the draft. Your app never sees those requests, and the one path reserved on that address is/__hatchik/— do not serve anything there. Every deploy to the draft hands your AI back an address it can fetch, and hands you a separate link that opens the draft in your browser. The live site is public and none of this applies to it.
Every site needs a 404 page
Include a file called 404.html. It is what somebody sees when they mistype an address or follow a dead link.
Without one, every address on the site answers with the home page and a success code. A visitor who mistypes a link sees something that looks like the site rather than "page not found", and a search engine is told the made-up address is a real page. The file only has to say the page isn't there and link back to the home page.
This applies to a simple website as much as to an app.
What happens when a publish fails
Hatchik starts the new container, polls /healthz until it times out, and watches the restart count for a crash loop. On failure it stops, keeps the previous version serving, marks the project's status as an error, and returns the failure with the last 200 lines of the container's log attached.
Read those lines before doing anything else. They almost always contain the real error. Do not guess at a cause, and do not republish the same code hoping for a different result.
If the fix is not obvious from the logs, hatchik_logs returns the current tail on demand, and hatchik_undo puts the last working version back while you work the problem out.
Publishing, in order
- Publish to the draft. That is the default and it is where work belongs.
- Give the customer the draft link and wait for them to look.
- When they approve, publish the draft to live — this makes live the exact files they previewed, rather than uploading a fresh copy nobody has seen.
Send the whole site every time. A publish carrying a note where the content should be — "rest of the file unchanged", "truncated for brevity" — is rejected, and rightly: it would replace a working site with a stub. Ordinary page code is never the problem; placeholder="Your name" on a form box is a normal part of a real file and always publishes.
Words to use with the customer
Draft and live. Publish. Go back. Your site, your app, your data. Not: deploy, environment, container, dev, prod, staging, instance, tier.
The same page as plain text, for an AI or anything else that prefers it: /docs/how-apps-run.md
All help pages
- Hatchik help
- What Hatchik is
- Connecting Claude to Hatchik
- Draft and live
- Going back
- Publishing failed
- My site is broken
- Keys, passwords and other settings
- Your own web address
- Email your app sends to your customers
- Letting people sign in to your app
- Paying for Hatchik
- Your data
- Locked out
- If a project is taken offline
- Limits
- How apps run on Hatchik
- Bringing a site you already have
- Your contact form