AdonisJS is a batteries-included TypeScript framework with a real ORM (Lucid), first-class migrations, and a structured project layout. That structure helps in production — but you still need to compile the app, run migrations in the right order, and connect to a database the platform can manage for you. Here's how to do it end to end.
Building AdonisJS for production
AdonisJS 6 compiles to plain JavaScript. The build command produces a self-contained build/ directory:
node ace buildThe output includes its own package.json. In production you install only production dependencies inside that folder:
cd build
npm ci --omit=dev
node bin/server.jsAdonis reads configuration from environment variables and validates them at boot via start/env.ts. If a required variable is missing, the app refuses to start — which is exactly what you want, rather than failing on the first request.
Configuring the database connection
Lucid reads PostgreSQL settings from environment variables. The defaults expect discrete values:
DB_HOST=...
DB_PORT=5432
DB_USER=...
DB_PASSWORD=...
DB_DATABASE=...Many managed platforms hand you a single DATABASE_URL connection string instead. You can parse it in config/database.ts:
import { defineConfig } from '@adonisjs/lucid'
import env from '#start/env'
const url = new URL(env.get('DATABASE_URL'))
export default defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: {
host: url.hostname,
port: Number(url.port),
user: url.username,
password: url.password,
database: url.pathname.slice(1),
ssl: { rejectUnauthorized: false }
},
migrations: { naturalSort: true }
}
}
})Running migrations safely
Never run migration:fresh in production — it drops every table. Use the forward-only command:
node ace migration:run --forceThe --force flag is required because Adonis refuses destructive operations in production unless you opt in. Run migrations as a separate step before the new app version starts taking traffic, not inside the app's boot sequence — otherwise two instances rolling out simultaneously can race on the same migration.
A Dockerfile for AdonisJS
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
ENV NODE_ENV=production
COPY --from=build /app/build ./
RUN npm ci --omit=dev
USER node
EXPOSE 3333
CMD ["node", "bin/server.js"]Adonis defaults to port 3333; set PORT and HOST=0.0.0.0 via environment variables so the platform can route to it.
Deploying on PandaStack
PandaStack pairs well with Adonis because the database is managed and auto-wired:
- 1Provision a managed PostgreSQL instance (14.x or 16.x) from the [dashboard](https://dashboard.pandastack.io).
- 2Connect your AdonisJS repo as a container app. PandaStack auto-detects Node, installs dependencies, and runs the build.
- 3Because the database is attached to the app,
DATABASE_URLis injected automatically — no copying credentials between dashboards. - 4Add a release step (or a one-off command) to run
node ace migration:run --forcebefore traffic shifts.
Images are built with rootless BuildKit in ephemeral Kubernetes Job pods and deployed with Helm, so there's no host Docker daemon involved.
Migration strategy on a managed database
| Step | Command | When |
|---|---|---|
| Apply schema changes | migration:run --force | Before new version goes live |
| Check status | migration:status | Anytime, read-only |
| Roll back last batch | migration:rollback | Recovery only |
Keep migrations backward-compatible across one release: add columns as nullable first, backfill, then enforce constraints in a later deploy. This lets old and new instances coexist during a rolling update.
Backups and connection limits
Managed PostgreSQL on PandaStack includes scheduled and manual backups (7-day retention on Free, longer on paid plans). Watch your connection count — the Free tier allows 50 connections. Lucid pools connections per instance, so if you scale to several instances, set a sensible pool.max in the connection config to avoid exhausting the limit.
References
- [AdonisJS deployment guide](https://docs.adonisjs.com/guides/getting-started/deployment)
- [Lucid ORM migrations](https://lucid.adonisjs.com/docs/migrations)
- [node-postgres connection pooling](https://node-postgres.com/features/pooling)
- [PostgreSQL: managing connections](https://www.postgresql.org/docs/current/runtime-config-connection.html)
Want your AdonisJS app and its database in one place? PandaStack's free tier includes a managed PostgreSQL database with automatic DATABASE_URL injection — start at [dashboard.pandastack.io](https://dashboard.pandastack.io).