This guide explains how to run GigTracker using Docker and Docker Compose.
Start all services:
docker compose up
This will start:
Access the application:
adminadmindocker compose up -d
docker compose down
# All services (live stream via Docker)
docker compose logs -f
# Specific service
docker compose logs -f backend
docker compose logs -f frontend
docker compose logs -f db
GigTracker writes structured OWASP-compliant JSON logs to the ./logs/ directory on the host (bind-mounted from the container's /app/logs/).
| File | Contents |
|---|---|
logs/gigtracker.log | All application logs (JSON lines) |
logs/error.log | ERROR and FATAL entries only |
logs/gigtracker.log.1 … .5 | Rotated backups |
Live viewing — logs are output to both files and stdout, so docker compose logs -f backend shows them in real time.
Log levels are configurable via the Django admin panel (Site Configuration → Logging Configuration) or the API (PUT /api/logs/config/):
| Level | Description |
|---|---|
| Info / Warn (default) | Standard operational logging |
| Debug | Verbose flow-level detail for troubleshooting |
| Trace | ⚠️ Maximum verbosity — captures request details. Generates large files. |
Rotation defaults: 10 MB per file, 5 backups (~50 MB total). Configurable via admin panel.
Log viewer UI: Navigate to /logs in the web app (admin users only) to browse, filter, search, and download logs.
Sensitive data masking: Tokens, API keys, passwords, credit card numbers, emails, and credential URLs are automatically redacted in all log output.
docker compose up --build
# Create migrations
docker compose exec backend poetry run python manage.py makemigrations
# Apply migrations
docker compose exec backend poetry run python manage.py migrate
# Create superuser
docker compose exec backend poetry run python manage.py createsuperuser
# Django shell
docker compose exec backend poetry run python manage.py shell
docker compose exec db psql -U postgres -d gigtracker
docker compose down -v
Both frontend and backend support hot reload:
./backend are automatically detected./frontend trigger automatic rebuildbackend/pyproject.tomldocker compose up --build backend
frontend/package.json or run:
docker compose exec frontend yarn add <package-name>
docker compose up --build frontend
If you get port conflicts, you can modify the ports in docker-compose.yml:
ports:
- "8001:8000" # Change host port (left side)
Reset the database:
docker compose down -v # Removes volumes
docker compose up # Recreates database
If you encounter permission issues with volumes on Linux:
sudo chown -R $USER:$USER backend/logs backend/media backend/static
Check the logs:
docker compose logs <service-name>
docker compose down
docker compose build --no-cache
docker compose up
┌─────────────────────────────────────────────────────────┐
│ Docker Compose │
├──────────────┬──────────────────┬────────────────────────┤
│ Frontend │ Backend │ Database │
│ (Vite) │ (Django) │ (PostgreSQL) │
│ Port 5173 │ Port 5353 │ Port 5433 │
│ │ │ │
│ Node 20 │ Python 3.12 │ PostgreSQL 16 │
│ Alpine │ Slim │ Alpine │
└──────────────┴──────────────────┴────────────────────────┘
Default values are set in docker-compose.yml. For custom configuration:
Copy .env.docker to .env:
cp .env.docker .env
Modify values as needed
Restart containers:
docker compose down
docker compose up
GigTracker includes a production-ready Docker setup that bundles the frontend and backend into a single container using Nginx + Gunicorn.
# 1. Copy and edit the environment file
cp .env.prod.example .env.prod
# Edit .env.prod — set SECRET_KEY, DB_PASSWORD, Firebase keys, etc.
# 2. Build the production image
docker compose -f docker-compose.prod.yml build
# 3. Start in the background
docker compose -f docker-compose.prod.yml up -d
The app will be available at http://localhost:8080.
docker run# Start PostgreSQL
docker run -d --name gigtracker_prod_db \
-e POSTGRES_DB=gigtracker \
-e POSTGRES_USER=gigtracker \
-e POSTGRES_PASSWORD=your-db-password \
-v gigtracker_pgdata:/var/lib/postgresql/data \
--network gigtracker \
postgres:16-alpine
# Start GigTracker
docker run -d --name gigtracker \
-p 8080:80 \
-e PUID=1000 \
-e PGID=1000 \
-e UMASK=002 \
-e TZ=America/New_York \
-e SECRET_KEY=your-secret-key \
-e DEBUG=False \
-e ALLOWED_HOSTS=yourdomain.com \
-e DB_NAME=gigtracker \
-e DB_USER=gigtracker \
-e DB_PASSWORD=your-db-password \
-e DB_HOST=gigtracker_prod_db \
-e DB_PORT=5432 \
-e GEMINI_API_KEY=your-gemini-key \
-v gigtracker_media:/app/media \
-v gigtracker_static:/app/staticfiles \
-v gigtracker_config:/config \
-v ./logs:/app/logs \
--network gigtracker \
--restart unless-stopped \
neuman1812/gigtracker:latest
| Variable | Description | Default |
|---|---|---|
PUID | User ID for file permissions | 1000 |
PGID | Group ID for file permissions | 1000 |
UMASK | File creation mask | 002 |
TZ | Timezone | America/New_York |
| Variable | Description | Default |
|---|---|---|
SECRET_KEY | Required. Django secret key | — |
DEBUG | Debug mode (never True in prod) | False |
ALLOWED_HOSTS | Comma-separated hostnames | * |
GEMINI_API_KEY | Google Gemini AI key for receipt OCR | — |
GUNICORN_WORKERS | Number of Gunicorn workers | 3 |
GUNICORN_TIMEOUT | Gunicorn request timeout (seconds) | 120 |
| Variable | Description | Default |
|---|---|---|
DB_NAME | Database name | gigtracker |
DB_USER | Database user | gigtracker |
DB_PASSWORD | Required. Database password | — |
DB_HOST | Database host | db |
DB_PORT | Database port | 5432 |
These are baked into the frontend JavaScript bundle during docker build. You must rebuild the image if these change.
| Variable | Description |
|---|---|
VITE_FIREBASE_API_KEY | Firebase API key |
VITE_FIREBASE_AUTH_DOMAIN | Firebase auth domain |
VITE_FIREBASE_PROJECT_ID | Firebase project ID |
VITE_FIREBASE_STORAGE_BUCKET | Firebase storage bucket |
VITE_FIREBASE_MESSAGING_SENDER_ID | Firebase messaging sender ID |
VITE_FIREBASE_APP_ID | Firebase app ID |
VITE_FIREBASE_MEASUREMENT_ID | Firebase measurement ID |
┌─────────────────────────────────────────────────┐
│ Production Container (:8080) │
│ │
│ Nginx (:80) │
│ / → React SPA (static files) │
│ /api/* → Gunicorn → Django │
│ /admin/* → Gunicorn → Django │
│ /static/* → Django collected static files │
│ /media/* → User uploads │
│ │
│ Gunicorn (:8000) → Django WSGI │
│ │
│ Logging → /app/logs/ (bind mount → ./logs/) │
│ gigtracker.log (JSON, rotating, all levels) │
│ error.log (JSON, rotating, ERROR+) │
│ │
│ Init: PUID/PGID → migrations → collectstatic │
└────────────────────┬──────────────────────────────┘
│
┌──────▼──────┐
│ PostgreSQL 16 │
└──────────────┘
| Mount | Container Path | Description |
|---|---|---|
gigtracker_media | /app/media | User-uploaded files (receipts, etc.) |
gigtracker_static | /app/staticfiles | Django collected static files |
gigtracker_config | /config | Persistent configuration |
./logs | /app/logs | Application log files (JSON, rotating) |
Note: The
./logsbind mount ensures logs persist on the host and are accessible outside the container for monitoring or aggregation tools. Log rotation is handled by the application — defaults to 10 MB per file with 5 backups.
appuser via gosuDEBUG=False is enforced in the production compose filepython:3.12-slim base for minimal attack surface# Pull latest image (if using pre-built from Docker Hub)
docker compose -f docker-compose.prod.yml pull
# Or rebuild from source
docker compose -f docker-compose.prod.yml build --no-cache
# Restart
docker compose -f docker-compose.prod.yml up -d
For issues or questions:
docker compose logsContent type
Image
Digest
sha256:d39002607…
Size
100.4 MB
Last updated
5 days ago
docker pull neuman1812/gigtracker