Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/juadariasmar/inventory_project/llms.txt

Use this file to discover all available pages before exploring further.

Docker Compose is the recommended approach for self-hosted environments where you want full control over the infrastructure without depending on Vercel or other managed platforms. The repository ships with a production-ready multi-stage Dockerfile and a docker-compose.yml that wires the application together with a PostgreSQL 17 database in a single command. This is ideal for on-premise deployments, internal tooling environments, or local integration testing that mirrors production behaviour.

What’s included

The Docker setup consists of two files at the root of the repository:

Dockerfile

A two-stage build: the builder stage installs all dependencies, generates the Prisma Client, and compiles the Next.js application. The runner stage copies only the built artifacts into a minimal node:20-alpine image and runs the app as the unprivileged nextjs user.

docker-compose.yml

Defines two services — app (the Next.js application on port 3000) and db (PostgreSQL 17 Alpine on port 5432) — plus a named volume for persistent database storage and a health check that prevents the app from starting before the database is ready.

Deployment steps

1

Clone the repository and create your .env file

Copy the example environment file and fill in the values for your environment:
cp .env.example .env
At a minimum you must set DB_PASSWORD, NEXTAUTH_SECRET, and NEXTAUTH_URL before the containers will start correctly. See the environment variable table below for all accepted variables.
2

Set the required environment variables

Open .env in your editor and configure the three variables that have no safe default:
# Strong password for the Postgres inventory_user account
DB_PASSWORD=a_very_strong_password_here

# Secret used to sign NextAuth session tokens (min 32 chars)
NEXTAUTH_SECRET=your_random_32_char_secret_here

# Public URL where the application will be reachable
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET has no default and Docker Compose will refuse to start if it is not set — the compose file declares it with the :? error-on-missing modifier.
3

Start the services

Run the following command from the project root:
docker compose up -d
Docker will build the application image (pulling node:20-alpine and postgres:17-alpine if not already cached), then start both containers in detached mode. The first build takes two to four minutes depending on your network and hardware.
4

Verify the services are running

Check that both containers started successfully:
docker compose ps
You should see inventory_app and inventory_db both reporting a running status. The application is available at http://localhost:3000 and Postgres is reachable at localhost:5432 with credentials inventory_user / <DB_PASSWORD>.
5

Migrations run automatically at startup

The CMD in the Dockerfile runs npx prisma migrate deploy before starting Next.js:
CMD ["sh", "-c", "npx prisma migrate deploy && npm start"]
All pending migrations are applied on every container start. Because the app service declares depends_on: db: condition: service_healthy, the database passes its pg_isready health check before migrations are attempted, so there is no race condition on cold starts.

docker-compose.yml

services:
  db:
    image: postgres:17-alpine
    container_name: inventory_db
    restart: unless-stopped
    environment:
      POSTGRES_USER: inventory_user
      POSTGRES_PASSWORD: ${DB_PASSWORD:-inventory_pass_dev}
      POSTGRES_DB: inventory
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U inventory_user -d inventory"]
      interval: 10s
      timeout: 5s
      retries: 5

  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: inventory_app
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      DATABASE_URL: postgresql://inventory_user:${DB_PASSWORD:-inventory_pass_dev}@db:5432/inventory?schema=public
      NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000}
      NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:?se requiere NEXTAUTH_SECRET}
      NODE_ENV: production
    ports:
      - "3000:3000"

volumes:
  postgres_data:
    name: inventory_postgres_data

Environment variables for Docker

The docker-compose.yml reads the following variables from your shell environment or .env file. Variables consumed directly by the application (such as RESEND_API_KEY or Neon Auth secrets) should be passed through the app.environment block or via an .env file at the project root.
VariableDefaultRequiredDescription
DB_PASSWORDinventory_pass_devStrongly recommendedPassword for the inventory_user Postgres account. The dev default is insecure — always override in production.
NEXTAUTH_URLhttp://localhost:3000Yes in productionCanonical public URL of the application. Must match the domain used in browser redirects.
NEXTAUTH_SECRET(none)RequiredSecret used to sign NextAuth.js session tokens. Compose will fail to start if this is unset.

Dockerfile stages explained

Stage 1 — builder (node:20-alpine)

FROM node:20-alpine AS builder
WORKDIR /app

RUN apk add --no-cache libc6-compat openssl

COPY package.json package-lock.json* ./
RUN npm ci

COPY prisma ./prisma
RUN npx prisma generate

COPY . .

ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
The builder stage installs all production and development dependencies (npm ci), then generates the Prisma Client from prisma/schema.prisma. The full source tree is copied in and npm run build compiles the Next.js application (which internally runs prisma generate && next build). Telemetry is disabled to keep build logs clean.

Stage 2 — runner (node:20-alpine)

FROM node:20-alpine AS runner
WORKDIR /app

RUN apk add --no-cache openssl && \
    addgroup -g 1001 -S nodejs && \
    adduser -S -u 1001 nextjs

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000

COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma

USER nextjs
EXPOSE 3000

CMD ["sh", "-c", "npx prisma migrate deploy && npm start"]
The runner stage starts from a fresh node:20-alpine image — the build tools and dev dependencies from Stage 1 are discarded. Only the compiled .next output, public assets, node_modules, the Prisma schema, and package.json are copied across. The application runs as the unprivileged nextjs user (UID 1001) rather than root, following container security best practices. openssl is installed in the runner because the Prisma query engine binary requires it.

Persistent data volume

The named volume inventory_postgres_data stores all Postgres data files under /var/lib/postgresql/data inside the inventory_db container. This volume persists across docker compose down and docker compose up cycles, so your data survives container restarts and image rebuilds. To wipe the database entirely and start fresh, run:
docker compose down -v
The -v flag removes all named volumes declared in the compose file, including inventory_postgres_data.

Health check

The db service uses pg_isready to report readiness:
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U inventory_user -d inventory"]
  interval: 10s
  timeout: 5s
  retries: 5
Docker checks every 10 seconds, waits up to 5 seconds for a response, and retries up to 5 times before marking the service unhealthy. The app service declares condition: service_healthy, so it will not start — and will not attempt migrations — until Postgres has passed at least one health check.

External services

Even when running entirely in Docker, Neon Auth and Upstash Redis must still be configured and reachable from your Docker host. Neon Auth handles user authentication and issues JWTs; Upstash Redis provides rate limiting for API routes. Both are cloud services that are not bundled into the compose stack.Set NEON_AUTH_BASE_URL, NEON_AUTH_COOKIE_SECRET, NEON_WEBHOOK_SECRET, UPSTASH_REDIS_REST_URL, and UPSTASH_REDIS_REST_TOKEN in your .env file and pass them through to the app service’s environment block in docker-compose.yml if you need these features in your self-hosted environment.

Build docs developers (and LLMs) love