Render can build a repository's Dockerfile or pull a prebuilt image. The Docker path keeps Nub available for dependency installation, build scripts, and the running service.

Dockerfile

Dockerfile
FROM ghcr.io/nubjs/nub:0.6.0

COPY --chown=node:node package.json package-lock.json ./
RUN nub ci

COPY --chown=node:node . .
CMD ["nub", "run", "start"]

Change package-lock.json to the project's lockfile. The HTTP server must listen on 0.0.0.0 and use PORT:

server.ts
import { createServer } from "node:http";

const port = Number(process.env.PORT || 10000);
createServer((request, response) => {
  if (request.url !== "/alive") {
    response.writeHead(404);
    response.end();
    return;
  }

  response.writeHead(200, { "content-type": "application/json" });
  response.end(JSON.stringify({
    alive: true,
    runtime: "nub-on-node",
  }));
}).listen(port, "0.0.0.0");

Dashboard deployment

  1. Select New → Web Service and connect the repository.
  2. Set Language to Docker, even though the application runs on Node.
  3. Leave Dockerfile Path empty for a root Dockerfile.
  4. Set Health Check Path to /alive.
  5. Create the service and request /alive on its assigned *.onrender.com URL.

Render uses the Dockerfile's CMD unless Docker Command overrides it. The default web-service port is 10000, but reading PORT also covers a custom value.

CLI deployment

Authenticate the Render CLI, then create the same repository-backed Docker service:

render login
render workspace set WORKSPACE_ID
render services create \
  --name nub-app \
  --type web_service \
  --repo https://github.com/OWNER/REPOSITORY \
  --branch main \
  --runtime docker \
  --plan free \
  --region oregon \
  --health-check-path /alive \
  --confirm \
  --output json

SERVICE_URL=$(render services --output json \
  | jq -r '.[] | select(.service.name == "nub-app") | .service.serviceDetails.url')
curl --fail "$SERVICE_URL/alive"
# {"alive":true,"runtime":"nub-on-node"}

Blueprint

A repository can declare the same service in render.yaml:

render.yaml
services:
  - type: web
    name: nub-app
    runtime: docker
    plan: free
    dockerfilePath: ./Dockerfile
    healthCheckPath: /alive

The free plan is available for web services and has platform limits. Omit plan only when the workspace should use Render's current default paid plan.

Prebuilt images

Select Existing Image to deploy an application image published to GHCR instead of building the Dockerfile on Render. Public images need no registry credential; private images require one in the Render workspace.

Prebuilt-image services do not receive automatic deploys from source commits. Trigger a redeploy after GitHub Actions publishes a new image.

As an alternative to the repository-backed command, create a service from a published image:

render services create \
  --name nub-app \
  --type web_service \
  --image ghcr.io/OWNER/nub-app:COMMIT_SHA \
  --plan free \
  --region oregon \
  --health-check-path /alive \
  --confirm \
  --output json

Use one creation path, not both. The render services lookup and /alive request from the CLI section verify either service.