Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tech-dipesh/yeti-Jobs/llms.txt

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

Yeti Jobs ships with Docker support for the Express backend, letting you package the Node.js server and its dependencies into a reproducible image that runs identically on any machine or cloud VM. The build uses a two-stage Dockerfile to keep the final image lean — dev dependencies are installed only during the build stage and are not carried into the production image.
Only the backend is fully containerised at this time. The frontend is a static Vite build that is deployed separately (e.g. Vercel), and the PostgreSQL database is expected to be an external service (local Postgres or Supabase). Partial Dockerization of the full stack via compose.yml is described at the end of this page.

Backend Dockerfile

The following Dockerfile lives at backend/Dockerfile. It uses a multi-stage build:
backend/Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run dev || true

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/src ./src
COPY --from=builder /app/server.js ./
COPY --from=builder /app/db ./db
COPY --from=builder /app/.env ./.env
EXPOSE 3000
CMD ["sh", "-c", "node db/migrate.js && node server.js"]

Stage breakdown

StageBase imagePurpose
buildernode:20-alpineInstalls all dependencies (including devDependencies) and runs any build steps
Final (unnamed)node:20-alpineInstalls production-only dependencies (--omit=dev) and copies the built artefacts
What each instruction does:
  • COPY package*.json ./ + RUN npm ci — installs a clean, deterministic dependency tree from package-lock.json.
  • RUN npm run dev || true — runs any build-time scripts; the || true ensures the stage does not fail if there is nothing to compile.
  • RUN npm ci --omit=dev — re-installs only production packages in the final stage, keeping the image size down.
  • COPY --from=builder — cherry-picks only the files the server needs: src/, server.js, db/, and .env.
  • EXPOSE 3000 — documents the port; map it with -p at docker run time.
  • CMD ["sh", "-c", "node db/migrate.js && node server.js"] — runs database migrations first, then starts the server.
The COPY --from=builder /app/.env ./.env instruction copies your local .env into the image. This is convenient for development but means the image contains secrets. For production, remove this line and inject variables at runtime using --env-file or your orchestrator’s secrets management.

Build and Run

1

Create your backend .env file

Before building, make sure backend/.env exists and contains all required variables. See the Environment Variables reference for the full list.
cd backend
cp .env.example .env
# Edit .env and fill in real values
2

Build the Docker image

Run this command from inside the backend/ directory. The -t flag tags the image as yeti-jobs-backend.
cd backend
docker build -t yeti-jobs-backend .
The build will take a minute on the first run while npm downloads packages. Subsequent builds are faster thanks to layer caching.
3

Run the container

Start the container in detached mode (-d), mapping host port 3000 to the container’s port 3000, and injecting environment variables from your local .env file.
docker run -d \
  -p 3000:3000 \
  --env-file .env \
  --name yeti-jobs-backend \
  yeti-jobs-backend
FlagPurpose
-dRun in the background (detached)
-p 3000:3000Expose the server on localhost:3000
--env-file .envInject all variables from your .env without baking them into the image
--name yeti-jobs-backendGive the container a friendly name for docker stop/docker logs
4

Verify the server is running

Tail the container logs to confirm the server and database connection started successfully.
docker logs -f yeti-jobs-backend
You should see:
Database connected successfully
Server running on port 3000
Then hit the health endpoint:
curl http://localhost:3000/api/v1/swagger
5

Stop and remove the container

docker stop yeti-jobs-backend
docker rm yeti-jobs-backend

Database connectivity

The backend container does not include PostgreSQL. The pg.Pool inside src/db.js connects to whichever host is referenced in DATABASE_PASSWORD (your connection string). Point this at:
  • A local Postgres instance: postgresql://postgres:password@host.docker.internal:5432/yeti_jobs — use host.docker.internal instead of localhost so the container can reach the host machine’s network.
  • A cloud-hosted Supabase database: use the Supabase connection pooler URL (port 6543). See Deploy to Vercel & Render for details.

Docker Compose (full local stack)

A compose.yml at the repository root orchestrates all three services — PostgreSQL, the backend, and the frontend — on a shared bridge network. This is the fastest way to spin up the entire application locally without installing Node or Postgres directly.
compose.yml
services:
  postgres:
    image: postgres:15-alpine
    container_name: yeti-postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
      POSTGRES_DB: yeti_jobs
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - yeti-network
    restart: unless-stopped

  backend:
    build: ./backend
    container_name: yeti-backend
    environment:
      PORT: 3000
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      URL_SUPABASE_CONNECT: ${URL_SUPABASE_CONNECT}
      ANON_KEY_SUPABASE: ${ANON_KEY_SUPABASE}
      NODEMAILER_MY_EMAIL: ${NODEMAILER_MY_EMAIL}
      NODEMAILER_MY_PASSWORD: ${NODEMAILER_MY_PASSWORD}
      NODEMAILER_MY_HOST: ${NODEMAILER_MY_HOST}
      JSON_SECRET_KEY: ${JSON_SECRET_KEY}
      CLIENT_BASE_URL: http://localhost:5173
      MAXAGE: ${MAXAGE}
      GROK_API: ${GROK_API}
    ports:
      - "3000:3000"
    depends_on:
      - postgres
    networks:
      - yeti-network
    restart: unless-stopped

  frontend:
    build: ./frontend
    container_name: yeti-frontend
    environment:
      VITE_SERVER_URL: ${VITE_SERVER_URL}
    ports:
      - "5173:5173"
    depends_on:
      - backend
    networks:
      - yeti-network
    restart: unless-stopped

volumes:
  postgres_data:

networks:
  yeti-network:
    driver: bridge
Compose reads variable values from a .env file in the repository root. Create one by merging your backend and frontend variables:
# In the repo root
cp backend/.env.example .env
echo "VITE_SERVER_URL=http://localhost:3000" >> .env
# Fill in all remaining values
Then start the entire stack:
docker compose up --build
ServiceLocal address
Frontend (Vite / serve)http://localhost:5173
Backend (Express)http://localhost:3000
PostgreSQLlocalhost:5432
To tear it all down and remove volumes:
docker compose down -v

Frontend Dockerfile (reference)

The frontend also ships with its own multi-stage Dockerfile at frontend/Dockerfile, used by Compose:
frontend/Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM node:20-alpine
RUN npm install -g serve
WORKDIR /app
COPY --from=builder /app/dist ./dist
EXPOSE 5173
CMD ["serve", "-s", "dist", "-l", "5173"]
The build stage compiles the Vite bundle; the final stage serves the static dist/ output with the serve package on port 5173.

Build docs developers (and LLMs) love