AdonisJS brings a Laravel-style, batteries-included experience to Node: a real ORM (Lucid), first-class migrations, validation, auth, and a CLI called ace. That structure makes deployment predictable once you know the build pipeline and the few environment variables Adonis insists on.
The build pipeline
Modern AdonisJS (v6) is TypeScript and compiles to a build/ directory. The production flow is:
# Install deps
npm ci
# Compile TypeScript to ./build
node ace build
# The build output is a self-contained app; install prod deps inside it
cd build && npm ci --omit=dev
# Start
node bin/server.jsThe node ace build command produces an optimized bundle. Your production process is plain node build/bin/server.js.
APP_KEY is mandatory
Adonis encrypts cookies, signs values, and powers its Encryption service with APP_KEY. The app will refuse to boot without it. Generate one:
node ace generate:keyThen set it as an environment variable in production. Never commit it, and keep it stable — rotating APP_KEY invalidates existing signed cookies and sessions.
Other core env vars:
NODE_ENV=production
PORT=3333
HOST=0.0.0.0
APP_KEY=<generated>
# Lucid (PostgreSQL example)
DB_HOST=...
DB_PORT=5432
DB_USER=...
DB_PASSWORD=...
DB_DATABASE=...If your platform provides a single DATABASE_URL, configure Lucid to parse it:
// config/database.ts
import { defineConfig } from '@adonisjs/lucid';
export default defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: process.env.DATABASE_URL
? process.env.DATABASE_URL
: { host: process.env.DB_HOST /* ... */ }
}
}
});Migrations with Lucid
Lucid migrations run through ace. In production, apply them as a deploy step:
node ace migration:run --forceThe --force flag is required in production to confirm you really mean to mutate the database. Run this once per deploy, before the new version takes traffic — not on every container start, or replicas will race.
A production Dockerfile
FROM node:20-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN node ace build
FROM node:20-slim
WORKDIR /app
COPY --from=build /app/build ./
RUN npm ci --omit=dev
ENV NODE_ENV=production
EXPOSE 3333
CMD ["node", "bin/server.js"]Note how prod dependencies are installed *inside* the compiled build/ output — that directory has its own package.json.
Deploying on PandaStack
- 1Provision a managed PostgreSQL (or MySQL — Lucid supports both). PandaStack injects
DATABASE_URL. - 2Connect the Git repo as a container app. The Dockerfile is auto-detected; without one, the Node buildpack runs
node ace buildand starts the server. - 3Set
APP_KEY,NODE_ENV=production, andHOST=0.0.0.0so the server binds correctly inside the container. - 4Run
node ace migration:run --forceonce before serving. - 5Add a custom domain; SSL is automatic.
The HOST=0.0.0.0 detail matters: bind to all interfaces, not localhost, or the platform's load balancer cannot reach your app.
Production checklist
- [ ]
APP_KEYset and stable. - [ ]
HOST=0.0.0.0and the rightPORT. - [ ] Migrations run with
--forceas a deploy step. - [ ] Prod dependencies installed inside
build/. - [ ]
NODE_ENV=production(enables optimizations and disables debug output). - [ ] Health-check route for the platform to probe.
A tiny health route helps:
// start/routes.ts
import router from '@adonisjs/core/services/router';
router.get('/health', () => ({ status: 'ok' }));Verifying
curl -s https://app.example.com/health
# {"status":"ok"}If that responds and your DB-backed routes work, the deploy is sound.
References
- [AdonisJS deployment guide](https://docs.adonisjs.com/guides/getting-started/deployment)
- [AdonisJS build process](https://docs.adonisjs.com/guides/concepts/typescript-build-process)
- [Lucid migrations](https://lucid.adonisjs.com/docs/migrations)
- [AdonisJS environment variables](https://docs.adonisjs.com/guides/getting-started/environment-variables)
---
AdonisJS's compiled output plus Lucid migrations deploy cleanly on PandaStack with a managed database and DATABASE_URL injected automatically. Start free at [dashboard.pandastack.io](https://dashboard.pandastack.io).