Dashboard

Build and deploy

Web apps and workers

What HCode expects from a web app or background worker: the port, health checks and how a deploy is verified.

Every project runs as a web app or a background worker. A web app gets an address and must answer HTTP on its port. A worker has no address and must keep running. You choose the type under App type when you create the project; the project settings don't change it.

Web app or background worker

Web app Background worker
For Websites and APIs Queue workers, bots, scheduled jobs
Address A generated address, plus any custom domains None
Port Required, and PORT is set None
Health check path Optional Not used
A deploy succeeds when The address answers The process starts and keeps running

HCode suggests Background worker when it finds no web framework or exposed port, for example a start script that runs a file called worker, queue, bot or scheduler, or a Python project using Celery, RQ or a Discord or Telegram bot library with no web framework. Check the suggestion; a web app created as a worker gets no address.

For a worker, the project page has no link to open and says "This is a background worker, so it has no web address. Check the logs to see what it's doing."

The port

A web app must listen on one port, on 0.0.0.0 rather than 127.0.0.1 or localhost. HCode sets the PORT environment variable to that port, so the simplest setup is to read it:

js
app.listen(process.env.PORT, "0.0.0.0");

PORT belongs to the platform. If you set your own PORT variable, the project's port replaces it when the app starts.

HCode picks the port in this order:

  1. The port you set, when you created the project or under Runtime port in Settings.
  2. For a static site served by HCode, 80.
  3. A port named in your start command: --port 8000, --port=8000, -p 8000, --listen 8000 or PORT=8000.
  4. Otherwise 3000.

An image's EXPOSE line only suggests a port while you create the project. It never changes the port of a running project.

The runtime port setting

Runtime port in Build configuration shows the port and where it came from, for example "Source: explicit" for a port you chose or "Source: start command". After the first successful deploy the port is Locked, and the field stays disabled until the project is stopped.

To change a locked port:

  1. Stop the project.
  2. Change Runtime port and click Save changes.
  3. Deploy. The port locks again once the new release is verified.

Saving a new port on a running project is refused with "Stop the project before changing its locked runtime port, then deploy it."

How a web app deploy is verified

After the new release starts, HCode requests the root of the generated address until it gets two answers in a row from your app. A 2xx, 3xx or 4xx answer passes, because an API with no root route answers 404 and a login-protected app 401. A 5xx answer fails, and so does no answer at all.

Some 4xx pages fail too, because every visitor would see them:

  • Django's "Bad Request (400)" page or DisallowedHost: add the generated address to ALLOWED_HOSTS.
  • Rails' "Blocked hosts" page: add the host to config.hosts.
  • Vite's "Blocked request. This host" page: add the host to server.allowedHosts or preview.allowedHosts.
  • webpack-dev-server's "Invalid Host header": run a production build and server instead of the dev server.

A failed check fails the deploy with a sentence naming the cause, such as "Your app's web address didn't respond. Most often the app isn't listening on the expected port." App won't start lists these messages and their fixes.

Health check path

Set Health check path (optional) under Settings if your app has a dedicated health endpoint, such as /health, /api/health or /healthz. With a path set, the check runs inside your container for as long as it runs and must pass as well as the address check above:

  • it requests http://127.0.0.1:<port><path> every 10 seconds, with a 5-second timeout;
  • the app has 60 seconds to start before failed checks count;
  • an answer below 400 passes, and 3 failures in a row mark the app unhealthy.

The check uses wget, curl, node or python3, whichever your image has. An image with none of them always fails it, so leave the path empty for very small images.

Rolling updates

A deploy starts the new release before it stops the old one, so a web app keeps serving while it updates. If the new release doesn't come up, the deploy fails and the previous release keeps running.

Background workers

A worker's start command must run a process that stays up, such as a queue consumer or a bot's event loop. A worker that exits fails the deploy with "Your background worker exited instead of staying running." or, with the exit code, "Your background worker exited with code 1 instead of staying running." The failed deployment shows the last lines the worker printed under Last output.

A command that runs once and exits, such as a one-off script, doesn't fit a worker. Check the runtime logs to see what a worker is doing.