An Express API needs almost nothing from a host beyond a process that runs npm start, a port it can bind to, and somewhere to send traffic once it is healthy. Ownkube handles all of that from a git push, with a managed Postgres available if the API needs one.
This walks through deploying a Node.js Express API on Ownkube Compute: the start command, the PORT environment variable, an optional database, a health check endpoint, and horizontal autoscaling.
TL;DR
- Ownkube runs
npm start(or your Dockerfile’sCMD) and expects the app to bind toprocess.env.PORT. - Add a managed Postgres if the API needs a database, and pass its connection string in as
DATABASE_URL. - Add a
/healthroute so Ownkube’s health checks can tell a live process from a stuck one. - Turn on horizontal autoscaling so the API adds instances under load and idles back down when quiet.
- Metered billing suits most APIs: a mostly-idle service between requests barely registers on the CPU meter.
1. Confirm the app binds to the right port
Ownkube injects a PORT environment variable into the container. The app has to read it rather than hardcoding a port:
// server.js
const express = require("express");
const app = express();
app.get("/health", (req, res) => {
res.status(200).json({ status: "ok" });
});
app.get("/", (req, res) => {
res.json({ message: "hello from Ownkube" });
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`listening on ${port}`);
});
Make sure package.json has a start script that Ownkube can run directly:
{
"scripts": {
"start": "node server.js"
}
}
2. Add a Dockerfile (optional, recommended for control)
You can deploy straight from source, but a Dockerfile gives you a pinned Node version and a smaller runtime image:
FROM node:20-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
FROM node:20-slim
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["npm", "start"]
3. Push and create the app
Connect the repo from the dashboard, or use okctl:
okctl login
okctl apps create express-api --dockerfile Dockerfile
okctl deploy
Without a Dockerfile, Ownkube can build straight from package.json and run npm start, which is enough for a simple API with no native dependencies.
Ownkube allocates a *.ownkube.app hostname with automatic TLS. Attach a custom domain later from the app’s settings if you need one.
4. Set environment variables
Set any config the API needs, including secrets, from the dashboard or the CLI. These are injected into the running container and never committed to the repo:
okctl env set NODE_ENV=production --app express-api
okctl env set API_KEY="your-secret-key" --app express-api
5. Add a managed Postgres, if the API needs one
Not every API needs a database, but if yours does, create a Postgres box from New > Database > Postgres in the dashboard. It is a single-instance, private (in-region) box starting at $4 a month plus storage at $0.15 per GB, backups included. Multi-instance HA and public access are on the roadmap, not shipped yet.
okctl env set DATABASE_URL="postgres://user:password@db-host:5432/api_production" --app express-api
With something like pg or an ORM such as Prisma or Drizzle, point the client straight at process.env.DATABASE_URL:
const { Pool } = require("pg");
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
6. Wire up the health check
Ownkube uses a health check endpoint to know when a new deploy is actually ready to take traffic, and to detect a process that is alive but stuck. Point it at the /health route from step 1:
okctl apps update express-api --health-check-path /health
A deploy only completes the rollout, and an old instance only gets terminated, once the new instance’s health check passes. That is what makes rollouts zero-downtime instead of a race between old and new processes.
7. Turn on autoscaling
Most APIs see uneven traffic through the day. Horizontal autoscaling adds instances when CPU or memory crosses a threshold and scales back down when the API is quiet:
okctl apps update express-api --autoscale-min 1 --autoscale-max 5 --autoscale-cpu-target 70
Combined with metered billing, an API that is busy for an hour a day and idle the rest of the time pays close to nothing on CPU during the idle hours, since metered pricing bills both CPU and memory on what is actually used, not on the instance size.
okctl deploy --cpu-limit 500m --memory-limit 512Mi
8. Check logs and metrics
Logs and per-instance metrics are in the Ownkube dashboard as soon as the app is deployed, which is usually where a bad health check or a crash loop shows up first, before a user reports it.
Moving to your own AWS later
If the API needs to run inside a specific AWS account, either your own or a customer’s, for compliance or contractual reasons, the same Dockerfile moves over with no rewrite. Ownkube provisions the instance in that AWS account and keeps the same deploy flow, logs, metrics, and health checks attached. Railway does not offer that path since it only runs on Railway’s own infrastructure. Our Railway alternative comparison goes through what changes and what does not when you make that move.
FAQ
Does Ownkube support WebSockets?
Yes. A standard Express app using ws or socket.io on the same HTTP server works without extra configuration, since Ownkube proxies the app’s single port.
How do I run database migrations for a Node API?
Set a release command, the same way you would run any one-off command before a deploy completes, for example npx prisma migrate deploy or npx knex migrate:latest:
okctl apps update express-api --release-command "npx prisma migrate deploy"
Can I run a background worker alongside the API?
Yes. Deploy the worker as a separate Ownkube app that shares the same DATABASE_URL, and optionally a Valkey cache box (from $2 a month) if the worker needs a queue.
What happens if I forget to set PORT?
You do not need to set PORT yourself. Ownkube injects it into the container, and the app should read process.env.PORT rather than hardcoding a value, as shown in step 1.
Is there a free tier for small APIs?
No, Compute has no free tier. The Personal plan is $5 a month and loads $5 of wallet credit you then spend, so the plan price is also usable balance, and unused credit rolls over and never expires.
Where Ownkube fits
An Express API mostly needs a place to run npm start, a port to bind, and a health check that tells the platform when it is actually ready, and Ownkube covers that without a cloud account or a YAML pipeline to write first. Add a managed Postgres if the API needs state, turn on autoscaling if traffic is uneven, and the rollout stays zero-downtime by construction. Deploy your first app.