Skip to content
Galley

Stacks / Node

Preview environments for Node.

Node or Bun frontends and APIs with Postgres and Redis.

  • Node 20
  • Postgres 16
  • Redis 7

Node apps — Express, Hono, Fastify, Next.js — are the best-tested preview flow in Galley. Container-friendly by default, build graphs are shallow, everything you’d expect to just work, does.

The config

version: 1

services:
  web:
    kind: web
    build:
      path: ./
    expose: 3000
    depends_on: [postgres, cache]
    env:
      DATABASE_URL: postgres://app:pw@postgres:5432/app
      REDIS_URL: redis://cache:6379/0
      NODE_ENV: production

  postgres:
    kind: database
    image: postgres:16-alpine
    expose: 5432
    env:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: pw
      POSTGRES_DB: app

  cache:
    kind: cache
    image: redis:7-alpine
    expose: 6379

The web container reaches Postgres and Redis by their galley.yml names on the env network — no IP wrangling, no compose link semantics to learn. A kind: web service gets a public route (pr-N-myrepo.preview.yourco.dev) plus screenshots once it’s healthy. The data services stay internal.

The usual gotcha

Dockerfile layer ordering. A naive COPY . . before RUN pnpm install invalidates the install layer on every source change, so every preview rebuild reinstalls node_modules from scratch.

The fix is to copy the lockfile first:

FROM node:20-alpine
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
CMD ["pnpm", "start"]

If you don’t have a Dockerfile at all, omit it — Galley falls back to language autodetect. It picks node:20, runs pnpm install, and uses the start script. Workable for many apps; opt into a Dockerfile when you need control over the image.

For Next.js specifically, use the standalone output mode — cuts the final image from ~1.2 GB to ~180 MB and ships only the server’s runtime files.