Docs

Containers

What your image needs, ports, files and settings.

Any container image that serves a website or API over HTTP can run as a site. HTTPS is handled for you: your app speaks plain HTTP on one port, and visitors reach it on https://.

What your image needs

Vue, React and other single-page apps

On a site’s page you can run up to three copies of an app that keeps no files on a volume, as far as its plan has room; add a Redis-compatible cache, which gives your app REDIS_URL; and see the private addresses your other sites can use to reach it. A frontend and backend are two sites connected this way: make the backend private so it has no public address, and send /api on the frontend to it, so browsers need no CORS setup. Databases can be MariaDB or PostgreSQL. For others such as MongoDB, use a hosted service and keep its login in the site’s Secrets.

Images must run as a numbered user other than root, with a line such as USER 1000 in the Dockerfile, and listen on a port above 1024. We refuse an image that would run as root, because a break-in to it would have more power on our servers. Folders your app writes to must belong to that user.

Build the site with Node and serve the files with nginx. Two files next to your package.json:

# Dockerfile
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginxinc/nginx-unprivileged:1.29-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 8080
# nginx.conf
server {
  listen 8080;
  root /usr/share/nginx/html;
  # Unknown paths get index.html, so the router can show them and a
  # reload of /about still works.
  location / {
    try_files $uri $uri/ /index.html;
  }
  location /assets/ {
    expires 1y;
    add_header Cache-Control "public, immutable";
  }
}

Build and push it (see Private image registry), then create a site with your own container, port 8080 and no database. For Create React App, the build folder is build rather than dist; for Angular, dist/<project>/browser.

Settings read by your front end, such as an API address, are fixed when the site is built (VITE_ variables), not read from the console’s environment variables.

Image names and tags

Enter the image without its tag, and the tag separately: image yourname/my-app, tag 1.4.0. A name with no registry or username, like my-app, means one of Docker Hub’s official images, which is rarely what you want. We check the image exists before deploying it.

Use a new tag for every release. Re-pushing the same tag (especially latest) does not redeploy your site.

Deploying a new version

On the site’s page, under Deploy a new version, change the tag (or the image or port) and press Deploy. The old version stops just before the new one starts, so expect a few seconds of downtime.

Files that must survive a restart

A container’s own filesystem is reset on every restart and deploy. If your app writes files it needs to keep (uploads, a SQLite file), set Folder to keep between restarts when creating the site, for example /data. That folder is kept, backed up nightly, and is 2 GB on Starter and 10 GB on Business. Your app must be able to write to it.

Environment variables

Set them as KEY=value lines when creating the site. They are stored in plain text in our deployment repository, so do not put passwords or API keys there. The database settings are added for you; see Databases.

Secrets

For passwords, API keys and tokens, use Secrets on the site’s page instead. Each secret reaches your app as an environment variable, like any other, but is stored encrypted on our servers and never in our deployment repository. Values are write-only: after you save one it is never shown again, and to change it you save it again with the new value. Saving or removing a secret restarts the site, which takes about a minute.

Health checks

We send visitors to your site once its port accepts connections, and restart it if the port stops answering for a minute. An app that keeps crashing is shown on the site’s page with its exit code.

Network

Your app can reach the internet (APIs, payment providers). Nothing but our web front door can reach your app, and only your own app can reach its database.

NextDatabases →