Deploy a Node.js API
This guide deploys a minimal Node.js API from source. It focuses on the Node-specific parts of a reliable container: binding to 0.0.0.0, using the platform-provided PORT, running as a non-root user, and exposing a health endpoint.
For a framework such as Express, Fastify, or NestJS, keep the same container and networking rules and replace server.js with your application entry point.
Prerequisites
Section titled “Prerequisites”- Install
1ctl - Authenticate with
1ctl auth login - Install
jqso you can read the generated URL from JSON output
Confirm your active account and organization:
1ctl auth status1. Create the project
Section titled “1. Create the project”Run these commands in a terminal:
mkdir satusky-node-apicd satusky-node-api
cat > package.json <<'JSON'{ "name": "satusky-node-api", "version": "1.0.0", "private": true, "scripts": { "start": "node server.js" }}JSON
cat > server.js <<'JS'const http = require('node:http');
const port = Number(process.env.PORT || 3000);
const server = http.createServer((request, response) => { response.setHeader('content-type', 'application/json');
if (request.url === '/health') { response.end(JSON.stringify({ status: 'ok', runtime: process.version })); return; }
response.statusCode = 404; response.end(JSON.stringify({ error: 'not found' }));});
server.listen(port, '0.0.0.0', () => { console.log(`listening on 0.0.0.0:${port}`);});JSBinding to 0.0.0.0 is required. Binding to localhost makes the process unreachable from the platform route and health checks.
2. Add a production container
Section titled “2. Add a production container”Create Dockerfile:
FROM node:22-alpine
WORKDIR /appENV NODE_ENV=production
COPY --chown=node:node package.json server.js ./
EXPOSE 3000USER nodeCMD ["npm", "start"]USER node avoids running the application as root. Keep --chown=node:node on copied application files; otherwise a restricted runtime user may not be able to read them.
Exclude local and sensitive files from the cloud build context:
cat > .dockerignore <<'IGNORE'node_modules.git.envIGNOREDo not put credentials in the image or build context. Use SatuSky secrets when your real application needs them.
3. Configure SatuSky
Section titled “3. Configure SatuSky”Create satusky.toml:
[app]name = "satusky-node-api"port = 3000
[build]dockerfile = "Dockerfile"The configured port must match the port exposed by the process. The organization and namespace come from your active 1ctl profile, so they do not belong in this file.
4. Build and deploy
Section titled “4. Build and deploy”From the project directory, run:
1ctl deploy --config satusky.toml --health-path /healthSatuSky uploads the build context, builds the Dockerfile in the cloud, pushes a private image for both linux/amd64 and linux/arm64, and submits the deployment. You do not need a local Docker daemon.
The command returns an operation ID and deployment ID after the deployment is accepted. Check reconciliation separately:
1ctl app status satusky-node-apiRun the status command again while the workload or public route is still
progressing. Before treating the generated hostname as public-ready, wait for
its DNS condition to be verified; a hostname reservation, pending,
nxdomain, wrong_target, or error is not a successful DNS result.
5. Verify the API
Section titled “5. Verify the API”Read the generated HTTPS URL from the deployment record:
APP_URL=$(1ctl -o json app get satusky-node-api | jq -r '.domain')echo "$APP_URL"Call the health endpoint:
curl --fail --show-error --silent "$APP_URL/health"A healthy response looks like:
{ "status": "ok", "runtime": "v22.23.1" }If the request is not ready yet, inspect status and logs before changing the application:
1ctl app status satusky-node-api1ctl logs --app satusky-node-api --tail 50The startup log should include:
listening on 0.0.0.0:30006. Deploy a code change
Section titled “6. Deploy a code change”Change the JSON response in server.js, then run the same deployment command:
1ctl deploy --config satusky.toml --health-path /healthSatuSky builds a new image and updates the existing application because the [app].name is unchanged. Deployment strategies and recovery are covered in the rollout and rollback guide rather than duplicated here.
7. Clean up
Section titled “7. Clean up”Delete the test application and its runtime resources:
1ctl app delete satusky-node-api --yesWhat you verified
Section titled “What you verified”- Source was built in the cloud without a local Docker daemon.
- The image supports both AMD64 and ARM64 machines.
- Node runs as a non-root user and listens on the correct network interface.
- The configured application port matches the Node process port.
/healthprovides a simple readiness and smoke-test endpoint.
Next steps
Section titled “Next steps”- Environment configuration for environment variables and secrets
- API with a database for a persistent backend
- CI/CD to automate tested deployments