Skip to content
Guides

Deploy an Express application

From an Express repository to a live URL. The one requirement is that your server listens where Kuppit routes.

Kuppit detects an Express application from the express dependency together with a start script, and starts it with that script. Express does nothing with PORT on its own, so the server has to.

What Kuppit detects

SettingValueFrom
FrameworkExpressThe express dependency and a start script. Without a start script, nothing is detected.
Node version24, or what engines.node in package.json admitsOnly engines.node is read. 24 and 22 are supported.
Package managerpnpm, npm or YarnThe lockfile, or the packageManager field.
Build commandYour build script, if there is oneA TypeScript compile, say. Absent for plain JavaScript.
Start commandnpm start, pnpm start or yarn startYour start script.
Port3000A default; PORT overrides it at runtime.

Before you deploy

Kuppit starts the container with PORT set and sends every request, and the health check, to that port. Listen on it, on all interfaces:

server.ts
import express from 'express'

const app = express()

app.get('/', (request, response) => {
    response.send('ok')
})

const port = Number(process.env.PORT ?? 3000)

app.listen(port, '0.0.0.0', () => {
    console.log(`listening on ${port}`)
})

A server that listens on a fixed port, or on localhost, starts fine and then fails the health check with The application did not become healthy. A start script that runs ts-node or tsx is fine as long as it is in dependencies rather than devDependencies; the build installs what production needs.

Deploy

Follow the Quickstart: install the GitHub App, choose the repository and branch, confirm the proposal, and choose Deploy analyzed commit. When the deployment reaches Live, the service's hostname answers.

Configuration

Everything your server reads from process.env comes from the service's Variables tab and what the environment provides. Add a value, mark it secret if it is one, and Redeploy now: a variable changes the next deployment, never the running one.

Add a database

On the environment's Overview, Add resource, PostgreSQL. Keep the name database. Add DATABASE_URL on the Variables tab as a Reference to databaseDATABASE_URL, then redeploy:

db.ts
import { Pool } from 'pg'

export const db = new Pool({ connectionString: process.env.DATABASE_URL })

A pool is the right shape for a long-running server; open it once at start-up rather than per request.

From here

Every push to the branch deploys again, and every pull request gets a preview environment. If the revision starts and then stops, or never answers, Start and health failures is the page.

Was this page helpful?