Dashboard

Build and deploy

Automatic builds

How HCode builds your app without a Dockerfile, and which languages and frameworks it detects.

With Automatic as the build type, HCode works out how to install, build and start your app from the files in your repository. You don't need a Dockerfile. This is the default for new projects unless HCode finds a Dockerfile.

What HCode recognises

When you create a project from GitHub, HCode reads the repository's manifests and proposes a plan. These are the setups it recognises, and the start command it suggests:

Your repository has Detected as Suggested start command
package.json with next Next.js your start script, or next start
svelte.config.js or @sveltejs/kit SvelteKit your start script, or node build
package.json with nuxt Nuxt your start script, or node .output/server/index.mjs
package.json with @nestjs/core NestJS your start script, or node dist/main.js
express, fastify, koa or @hapi/hapi Node.js server your start script, or node <file> for main, index.js, server.js or app.js when the file exists
vite, react-scripts or astro, and no server framework Static site none, the output is served as files
manage.py or django in your Python manifest Django none; migrations run and gunicorn serves the app when it starts
fastapi FastAPI uvicorn main:app --host 0.0.0.0 --port $PORT when main.py exists
flask and app.py Flask gunicorn app:app --bind 0.0.0.0:$PORT
go.mod Go none, the compiled binary runs
Gemfile with rails Ruby on Rails bundle exec rails server -b 0.0.0.0 -p $PORT
composer.json PHP none
index.html at the root and nothing else Static site none

When nothing more specific applies, a start script in package.json is suggested as npm start. A Procfile with a web: line wins over any suggestion: the build starts that process. Python manifests are requirements.txt, pyproject.toml or Pipfile, and Poetry is recognised.

Java projects built with Maven or Gradle also build, though the plan step doesn't name them. Bun projects with a bun.lock or bun.lockb build with the newest Bun release unless you choose a version. For language versions, see Runtime versions.

Detection only fills in the plan. Your settings decide what is built, and you can change them under Settings.

Install, build and start

Dependencies are installed from your lockfile without changing it, so production builds are reproducible. If package.json and the lockfile disagree, the build fails rather than quietly installing different versions; run your package manager locally and commit the updated lockfile. The package manager comes from the packageManager field in package.json, or from the lockfile: pnpm-lock.yaml, yarn.lock or package-lock.json.

You can override each stage under Settings, in Build configuration:

  • Install command replaces the install step, for example npm install --legacy-peer-deps for peer dependency conflicts. It isn't used for Python and PHP projects, whose install step can't be replaced; the build log says so.
  • Build command replaces the build step. When package.json has a build script, the default npm run build runs it with your own package manager, so it works for pnpm, Yarn and Bun projects too.
  • Start command (optional) is what runs your app, and it overrides whatever the build produced. Leave it empty unless you need a specific command. A static site has no node binary, so a command such as node index.js stops it from starting.
  • Output directory is the folder a static build writes to, such as dist. See Static sites.

Framework preset fills those four fields for Next.js, Astro, SvelteKit, Nuxt, Remix, Vite, a Node.js server or a plain static site. For a GitHub project, Node.js (Express, Fastify…) looks up your repository's start command instead of guessing one. Save, then deploy again for a change to take effect.

Environment variables in the build

The project's environment variables are available to every build command, so a build that reads NEXT_PUBLIC_* or DATABASE_URL sees your values. They are passed as build secrets and are not written into the built image.

A few names, such as PORT and PATH, and empty or very large values are left out of the build and only reach the running app; Environment variables lists them. The build log names any variable it left out. Changing a variable changes the build, so the next deploy doesn't reuse a cached result built with the old value.

Databases during the build

HCode databases can't be reached while your app is being built, only once it runs. Run migrations and database checks from your start command, not your build command. For example:

bash
npx prisma migrate deploy && npm start

For Django, the build runs collectstatic at the end of the build step, and migrations run when the app starts. Set DISABLE_COLLECTSTATIC=1 as an environment variable to skip collectstatic, or set your own build command, in which case HCode leaves the build step alone. A failing collectstatic prints a warning and the build carries on.

Build time

Build time counts toward your plan's build minutes, and each build has a maximum duration. Build minutes explains both. When a build fails, Build failures explains the messages HCode shows.