In this tutorial you take a small HTTP service from a folder on your laptop to a production URL with its own domain and a Postgres next to it. Along the way you meet everything the Applications plane does for you: the build, the health check, preview releases, promotion, variables, volumes and rollback. Nothing here needs Kubernetes, a registry account or a YAML file.
1. Write the service#
Any program that listens on a TCP port can be an application. The only contract is: read the port from the PORT environment variable and answer HTTP on it. Create a folder, run npm init -y, and save this as server.js:
const http = require('node:http'); // The platform tells the container which port to listen on. const port = Number(process.env.PORT || 3000); http.createServer((req, res) => { if (req.url === '/healthz') { // The health check: anything below 500 means "serving". res.writeHead(200, { 'content-type': 'text/plain' }); res.end('ok'); return; } res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ hello: 'world', at: new Date().toISOString() })); }).listen(port, () => console.log(`listening on ${port}`));
The /healthz route is not decoration. After every deploy the platform polls a health check and only then routes traffic to the new release. A dedicated endpoint that answers quickly, without touching the database, makes deploys boring in the best sense. Next to the server put a Dockerfile:
FROM node:22-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . ENV NODE_ENV=production EXPOSE 3000 CMD ["node", "server.js"]
That is the whole project: two files plus package.json. If you already have a service with its own Dockerfile, use it as is. Builds run on the platform, so the Dockerfile may be as elaborate as you like, including multi-stage builds.
2. Create the application#
An application is a named slot that releases land in. Create it once, with the port your program listens on and the path of the health check. With the CLI it is one line, shown in the next step together with the deploy. Ports, health checks and resource limits can be changed later.
In the dashboard open Applications, pick or create a project, and press + Create on the canvas. Choose Empty service if you will push a build from your machine, GitHub repository to build from a repo, or Docker image to run an image that already exists. Give it a name and the port, and the service appears on the canvas with a panel on the right for everything that follows.
3. Deploy#
Run the commands below inside the project folder. The deploy packs the folder, sends it to the builders, builds the Dockerfile, starts a container for the new release, waits for the health check and prints a preview URL.
# 1. The application: an HTTP service on port 3000, health-checked at /healthz inquir apps create web --port 3000 --health-path /healthz # 2. Build the Dockerfile in this directory on the platform's builders and # release the image. The command waits for the health check to pass and # prints the preview URL of the new release. inquir apps deploy web # 3. Look around inquir apps status web # releases, endpoints, hostnames inquir apps logs web --tail 100 # container logs # 4. Take production (owner or admin), then give it a public domain inquir apps promote <releaseId> inquir apps update web --ingress public
Watch the log stream as it goes: snapshot of your files, build with the Docker output, start, health, and finally the preview address. Every deploy creates a new immutable release; the previous ones stay around, which is what makes rollback instant later.
In the dashboard the same thing lives under the Deploy button in the service panel: New release takes an image reference, Build from source uploads a folder, and Deploy from GitHub builds a branch. The Deployments tab lists every release with its build log.
4. Check the preview#
Open the preview URL from the output. It serves the new release only, so you can click around, run smoke tests or share the link with a teammate before anyone else sees it. If something looks off, inquir apps logs web --tail 100 shows the container output, and inquir apps status web shows releases, endpoints and hostnames. The Logs and Deployments tabs in the service panel show the same.
5. Promote to production#
A release becomes production when you promote it. Take the release id from the deploy output or from inquir apps status web and run inquir apps promote <releaseId>. Traffic on the production address switches to the new container; the previous release stays available for rollback. Promotion is limited to workspace owners and admins on purpose, so a preview cannot turn into production by accident.
Every application has a private address inside the workspace, web.apps.internal, that only your other applications and functions can reach. Public exposure is opt-in: inquir apps update web --ingress public gives it a public hostname with TLS. Keep internal APIs private and open only what users need.
In the dashboard: Deployments tab, pick the release, Promote. Public access is under Settings → Networking, where Generate Domain issues a public hostname on the spot. If you want every successful deploy to go straight to production, deploy with --promote.
6. Configuration and secrets#
Configuration lives in variables, not in the image. Open the Variables tab of the service, add a key such as LOG_LEVEL=debug and press Save & redeploy: a new release starts with the new environment, passes the health check and takes over. Values are stored encrypted and never end up in build logs.
From the terminal, pass --set KEY=VALUE when creating or updating the application. Variables that only differ between preview and production are best kept out of the image as well: the same release then runs identically everywhere, which is exactly why rollback is safe.
7. Add a database#
Databases are applications too, created from templates that already have a persistent volume and a private TCP endpoint. Create a Postgres, deploy it and connect it to the service:
# A Postgres from the template: a persistent volume and a private TCP endpoint inquir apps create db --template postgres inquir apps deploy db # Wire it into the service: DATABASE_URL lands in web's variables inquir apps connect db web inquir apps deploy web # the next release picks the variable up # Later on inquir apps backups db # snapshots: automatic every 6 h, newest 7 kept inquir apps domain web shop.example.com # bind your hostname, prints the DNS records inquir apps domain web shop.example.com --verify # once the records are live: routed, TLS issued inquir apps rollback web # back to the previous production release
The connect command puts a ready DATABASE_URL into the service variables; the next release picks it up. On the canvas the two services are now joined by an edge, and the database panel shows the private address, credentials and the Backups tab. Snapshots are taken every six hours and the newest seven are kept; a restore is one click, or inquir apps backups db in the terminal.
8. Your own domain#
Bind a hostname with inquir apps domain web shop.example.com. The command prints the DNS records to create at your registrar: a CNAME for the hostname and a TXT record proving you own it. Once they resolve, run the same command with --verify; routing switches and a certificate is issued automatically. The same lives in Settings → Networking → Custom domains, with the records shown next to the status.
9. When something goes wrong#
A bad release is undone with inquir apps rollback web or Roll back in the Deployments tab: production points at the previous release again within seconds, no rebuild needed. A few things to check when a deploy does not go through:
- Health check never green. The container starts but the platform cannot reach the port. Make sure the program listens on
0.0.0.0and onprocess.env.PORT, not a hard-coded value, and that the health path returns before any slow initialisation. - Build fails. The build log in the Deployments tab contains the full Docker output. Most failures are files missing from the snapshot (check
.dockerignore) or a dev dependency that the production install skipped. - Works on the preview, 404 on the domain. The application is still private. Open ingress in Settings → Networking or with
--ingress public, then check that the DNS records are verified. - Crash on start with a missing variable. Variables added after a deploy only reach the next release. Press Save & redeploy, or run
inquir apps redeploy web.
Where to go next#
The Applications reference covers TCP endpoints, the console, resource limits and the API behind every command. The CLI page lists all flags. And if part of your system is better off as event-driven code without a server, the next tutorial writes and deploys a function and puts it behind the same gateway.