Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
node_modules
dist
.git
.github
coverage

docker
docs
test

.env*
tools
jest.config.ts
Taskfile.yml

*.md
!README.md
79 changes: 79 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# syntax=docker/dockerfile:1

ARG NODE_IMAGE=node:20-alpine

# -----------------------------
# Stage 1 — Install dependencies
# -----------------------------
FROM ${NODE_IMAGE} AS deps

RUN corepack enable pnpm

WORKDIR /app

COPY package.json pnpm-lock.yaml ./

RUN pnpm install --frozen-lockfile


# -----------------------------
# Stage 2 — Build application
# -----------------------------
FROM ${NODE_IMAGE} AS builder

RUN corepack enable pnpm

WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

RUN pnpm build


# -----------------------------
# Stage 3 — Production deps only
# -----------------------------
FROM ${NODE_IMAGE} AS prod-deps

RUN corepack enable pnpm

WORKDIR /app

COPY package.json pnpm-lock.yaml ./

RUN pnpm install --prod --frozen-lockfile


# -----------------------------
# Stage 4 — Production runtime
# -----------------------------
FROM ${NODE_IMAGE} AS production

ENV NODE_ENV=production
ENV PORT=3000

WORKDIR /app

# production dependencies
COPY --chown=node:node --from=prod-deps /app/node_modules ./node_modules

# compiled application
COPY --chown=node:node --from=builder /app/dist ./dist

# migrations (needed for runtime migrations if used)
COPY --chown=node:node --from=builder /app/migrations ./migrations

# optional runtime metadata
COPY --chown=node:node package.json ./

EXPOSE ${PORT}

# healthcheck using your /health endpoint
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD wget -qO- http://localhost:${PORT}/health || exit 1

# run as non-root user
USER node

CMD ["node", "dist/main.js"]
1 change: 0 additions & 1 deletion docker/docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
services:
postgres:
image: postgres:16
container_name: reservation-dev-postgres
restart: unless-stopped
environment:
POSTGRES_DB: reservation_db
Expand Down
34 changes: 33 additions & 1 deletion docker/docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,45 @@
services:

postgres:
image: postgres:16
container_name: reservation-prod-postgres
restart: unless-stopped
environment:
POSTGRES_DB: reservation_db
POSTGRES_USER: reservation_app
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
volumes:
- postgres_prod_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U reservation_app -d reservation_db"]
interval: 5s
timeout: 5s
retries: 5

migrate:
image: reservation-api
container_name: reservation-prod-migrate
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: postgres://reservation_app:${DATABASE_PASSWORD}@postgres:5432/reservation_db
command: ["node", "dist/tools/migrate.js"]
restart: "no"

api:
image: reservation-api
container_name: reservation-prod-api
depends_on:
postgres:
condition: service_healthy
migrate:
condition: service_completed_successfully
environment:
NODE_ENV: production
DATABASE_URL: postgres://reservation_app:${DATABASE_PASSWORD}@postgres:5432/reservation_db
ports:
- "3000:3000"
restart: unless-stopped

volumes:
postgres_prod_data:
1 change: 0 additions & 1 deletion docker/docker-compose.test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
services:
postgres_test:
image: postgres:16
container_name: barber_test_db
restart: no
environment:
POSTGRES_DB: barber_test
Expand Down
Loading