The project uses Docker Compose to provide local infrastructure for development. Two services are defined: a PostgreSQL database and a Mailpit SMTP server for local email testing.
Services
db PostgreSQL 16 (Alpine). Stores all fleet management data. Persisted via the postgres_data named volume.
mailpit Local SMTP server and web UI for catching outgoing emails during development. No emails are delivered externally.
Port mappings
Service Port Purpose db 5432 PostgreSQL connections mailpit 1025 SMTP (send emails to) mailpit 8025 Mailpit web UI
Open http://localhost:8025 in your browser to inspect captured emails.
Starting services
Start the database
This starts PostgreSQL with the postgres_data volume mounted at /var/lib/postgresql/data. Data persists across container restarts.
Start the mail server
docker-compose up -d mailpit
Point your backend SMTP_HOST to 127.0.0.1 and SMTP_PORT to 1025.
Start all services at once
Starts both db and mailpit in the background.
docker-compose.yml
version : "3.9"
services :
db :
image : postgres:16-alpine
restart : unless-stopped
environment :
POSTGRES_DB : fleet_management
POSTGRES_USER : postgres
POSTGRES_PASSWORD : postgres
ports :
- "5432:5432"
volumes :
- postgres_data:/var/lib/postgresql/data
mailpit :
image : axllent/mailpit:latest
restart : unless-stopped
ports :
- "1025:1025"
- "8025:8025"
volumes :
postgres_data :
Backend Dockerfile
The backend image is built from packages/backend/Dockerfile. Here is what each stage does:
FROM node:20-alpine
WORKDIR /app
# Copy manifests first to leverage Docker layer caching
COPY package.json yarn.lock ./
COPY packages/backend/package.json packages/backend/package.json
COPY shipfree/core/package.json shipfree/core/package.json
# Install all workspace dependencies
RUN yarn install --frozen-lockfile || yarn install
# Copy application source
COPY packages/backend packages/backend
COPY shipfree/core shipfree/core
WORKDIR /app/packages/backend
# Create the uploads directory for file storage
RUN mkdir -p uploads
EXPOSE 3001
# On startup: run pending Prisma migrations, then start the server
CMD [ "sh" , "-lc" , "npx prisma migrate deploy && yarn start" ]
The image:
Uses Node 20 on Alpine Linux for a small footprint.
Copies package.json manifests before source code so dependencies are cached unless lockfiles change.
Includes the shipfree/core shared package alongside the backend.
Creates an uploads directory for user-uploaded files (mounted as a bind volume in production).
Runs prisma migrate deploy automatically on every container start before launching the server.
Exposes port 3001.
Environment variables for Docker-based setup
Set these in packages/backend/.env when running against the Docker Compose database and Mailpit:
PORT = 3001
NODE_ENV = development
DB_SYNC = false
DB_SEED = false
DATABASE_URL = postgresql://postgres:[email protected] :5432/fleet_management? schema = public
DB_HOST = 127.0.0.1
DB_PORT = 5432
DB_NAME = fleet_management
DB_USER = postgres
DB_PASSWORD = postgres
JWT_SECRET = change-me
DEFAULT_LOGIN_PASSWORD = changeme123
SMTP_HOST = 127.0.0.1
SMTP_PORT = 1025
SMTP_SECURE = false
SMTP_USER =
SMTP_PASS =
EMAIL_FROM = [email protected]
The default PostgreSQL credentials (postgres / postgres) are intended for local development only. Never use these credentials in staging or production.
Volumes
The postgres_data named volume persists your database across docker-compose down and container restarts. To fully reset local data, remove the volume explicitly: