18 KiB
18 KiB
Stack Templates Reference
This document provides ready-to-use templates for common stack configurations.
Table of Contents
- docker-compose.yml Templates
- .env.example Templates
- Service Configuration Templates
- Documentation Templates
- Git Configuration Templates
docker-compose.yml Templates
Minimal Stack (Development)
services:
app:
image: myapp:latest
container_name: ${PROJECT_NAME:-app}_main
restart: unless-stopped
environment:
- NODE_ENV=development
ports:
- "${APP_PORT:-3000}:3000"
networks:
- app-network
networks:
app-network:
driver: bridge
Web Stack (nginx + Application)
services:
nginx:
image: nginx:alpine
container_name: ${PROJECT_NAME:-app}_nginx
restart: unless-stopped
ports:
- "${NGINX_HTTP_PORT:-80}:80"
- "${NGINX_HTTPS_PORT:-443}:443"
volumes:
- ./config/nginx:/etc/nginx/conf.d:ro
- ./ssl:/etc/nginx/ssl:ro
networks:
- app-network
depends_on:
- app
app:
image: myapp:latest
container_name: ${PROJECT_NAME:-app}_main
restart: unless-stopped
environment:
- NODE_ENV=production
networks:
- app-network
networks:
app-network:
driver: bridge
Full Stack (nginx + App + PostgreSQL + Redis)
services:
nginx:
image: nginx:alpine
container_name: ${PROJECT_NAME:-app}_nginx
restart: unless-stopped
ports:
- "${NGINX_HTTP_PORT:-80}:80"
- "${NGINX_HTTPS_PORT:-443}:443"
volumes:
- ./config/nginx:/etc/nginx/conf.d:ro
networks:
- app-network
depends_on:
- app
app:
image: myapp:latest
container_name: ${PROJECT_NAME:-app}_main
restart: unless-stopped
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=${POSTGRES_DB}
- REDIS_HOST=redis
- REDIS_PORT=6379
secrets:
- db_password
- redis_password
networks:
- app-network
depends_on:
- postgres
- redis
postgres:
image: postgres:16-alpine
container_name: ${PROJECT_NAME:-app}_postgres
restart: unless-stopped
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
secrets:
- source: db_password
target: /run/secrets/db_password
volumes:
- ./config/postgres:/docker-entrypoint-initdb.d:ro
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: ${PROJECT_NAME:-app}_redis
restart: unless-stopped
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./config/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
- redis_data:/data
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
app-network:
driver: bridge
volumes:
postgres_data:
driver: local
redis_data:
driver: local
secrets:
db_password:
file: ./secrets/db_password
redis_password:
file: ./secrets/redis_password
Stack with Docker Secrets (Production)
services:
app:
image: myapp:latest
container_name: ${PROJECT_NAME:-app}_main
restart: unless-stopped
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_USER=${DB_USER}
# NO PASSWORDS IN ENVIRONMENT!
secrets:
- db_password
- api_key
- jwt_secret
networks:
- app-network
postgres:
image: postgres:16-alpine
container_name: ${PROJECT_NAME:-app}_postgres
restart: unless-stopped
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
# Password via Docker secret, not environment!
secrets:
- source: db_password
target: /run/secrets/postgres-passwd
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres_data:
driver: local
secrets:
db_password:
file: ./secrets/db_password
api_key:
file: ./secrets/api_key
jwt_secret:
file: ./secrets/jwt_secret
.env.example Templates
Basic Application
# Project Configuration
PROJECT_NAME=myapp
# Application Settings
NODE_ENV=production
APP_PORT=3000
# IMPORTANT: Copy this file to .env and configure
# .env is gitignored and should contain actual values
Web Stack with nginx
# Project Configuration
PROJECT_NAME=mywebapp
# nginx Configuration
NGINX_HTTP_PORT=80
NGINX_HTTPS_PORT=443
# Application Settings
NODE_ENV=production
# IMPORTANT: Copy this file to .env and configure for your environment
# .env is gitignored and should contain actual configuration
Full Stack (nginx + App + PostgreSQL + Redis)
# Project Configuration
PROJECT_NAME=myapp
# nginx Configuration
NGINX_HTTP_PORT=80
NGINX_HTTPS_PORT=443
# Application Settings
NODE_ENV=production
# PostgreSQL Configuration
POSTGRES_DB=myapp_db
POSTGRES_USER=myapp_user
# NOTE: Password stored in Docker secret, not here!
# Redis Configuration
REDIS_PORT=6379
# NOTE: Password stored in Docker secret, not here!
# IMPORTANT SECURITY NOTES:
# 1. Copy this file to .env for actual configuration
# 2. NEVER put secrets/passwords here - use Docker secrets in ./secrets/
# 3. .env is gitignored and should NEVER be committed
# 4. Keep .env and .env.example keys synchronized
Service Configuration Templates
nginx - Simple Reverse Proxy
File: config/nginx/default.conf
upstream app {
server app:3000;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
nginx - Production with SSL
File: config/nginx/default.conf
upstream app {
server app:3000;
keepalive 64;
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
# HTTPS Server
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL Configuration
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Proxy Configuration
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Static files caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
proxy_pass http://app;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
PostgreSQL - Initialization Script
File: config/postgres/init.sql
-- Initialize database
-- Create extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";
-- Create schemas
CREATE SCHEMA IF NOT EXISTS app;
-- Create tables
CREATE TABLE IF NOT EXISTS app.users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Create indexes
CREATE INDEX idx_users_email ON app.users(email);
-- Grant permissions
GRANT ALL PRIVILEGES ON SCHEMA app TO ${POSTGRES_USER};
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA app TO ${POSTGRES_USER};
Redis - Production Configuration
File: config/redis/redis.conf
# Redis Production Configuration
# Network
bind 0.0.0.0
protected-mode yes
port 6379
# General
daemonize no
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
# Snapshotting
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /data
# Replication
replica-serve-stale-data yes
replica-read-only yes
# Security
# requirepass will be set via environment variable
# Use Docker secrets for password
# Limits
maxmemory 256mb
maxmemory-policy allkeys-lru
maxclients 10000
# Append Only Mode
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# Slow Log
slowlog-log-slower-than 10000
slowlog-max-len 128
Documentation Templates
docs/setup.md
# Setup Instructions
## Prerequisites
- Docker Engine 20.10+
- Docker Compose V2
- Git
- [Other prerequisites]
## Quick Start
\`\`\`bash
# Clone repository
git clone <repository-url>
cd <project-name>
# Copy environment template
cp .env.example .env
# Configure environment
nano .env
# Set up Docker secrets (if applicable)
# Follow instructions in ./secrets/README.md
# Install git hooks
./scripts/setup-hooks.sh
# Validate stack
./scripts/validate-stack.sh
# Start services
docker compose up -d
# Check status
docker compose ps
\`\`\`
## Detailed Setup
### 1. Environment Configuration
Edit `.env` with your settings:
\`\`\`bash
PROJECT_NAME=myapp
# Add other variables from .env.example
\`\`\`
### 2. Secrets Configuration
Create required secrets in `./secrets/`:
\`\`\`bash
# Generate secure random password
openssl rand -base64 32 > ./secrets/db_password
# Set proper permissions
chmod 600 ./secrets/*
\`\`\`
### 3. Validation
Always validate before deploying:
\`\`\`bash
./scripts/validate-stack.sh
\`\`\`
### 4. Deployment
\`\`\`bash
docker compose up -d
\`\`\`
## Troubleshooting
### Services Won't Start
\`\`\`bash
# Check logs
docker compose logs
# Check specific service
docker compose logs <service-name>
\`\`\`
### Validation Fails
\`\`\`bash
# Run individual validators
claude-code run stack-validator
claude-code run secrets-manager --validate
claude-code run docker-validation
\`\`\`
### Permission Issues
\`\`\`bash
# Fix file ownership
sudo chown -R $USER:$USER .
# Re-validate
./scripts/validate-stack.sh
\`\`\`
docs/services.md
# Services Documentation
## Service Overview
| Service | Port | Purpose | Configuration |
|---------|------|---------|---------------|
| nginx | 80, 443 | Web server & reverse proxy | ./config/nginx |
| app | 3000 (internal) | Application server | Environment variables |
| postgres | 5432 (internal) | Database | ./config/postgres |
| redis | 6379 (internal) | Cache & sessions | ./config/redis |
## Service Details
### nginx
**Image**: nginx:alpine
**Purpose**: Web server and reverse proxy
**Configuration**: ./config/nginx/default.conf
**Secrets**: None
**Volumes**:
- ./config/nginx:/etc/nginx/conf.d:ro
- ./ssl:/etc/nginx/ssl:ro (if using HTTPS)
**Health Check**: HTTP request to port 80
### Application
**Image**: myapp:latest
**Purpose**: Main application server
**Configuration**: Environment variables in .env
**Secrets**:
- db_password
- redis_password
- api_key
- jwt_secret
**Dependencies**:
- postgres (database)
- redis (cache)
### PostgreSQL
**Image**: postgres:16-alpine
**Purpose**: Primary database
**Configuration**: ./config/postgres/init.sql
**Secrets**:
- db_password
**Volumes**:
- postgres_data:/var/lib/postgresql/data (persistent)
- ./config/postgres:/docker-entrypoint-initdb.d:ro (init scripts)
**Health Check**: `pg_isready` command
**Backup**:
\`\`\`bash
docker compose exec postgres pg_dump -U ${POSTGRES_USER} ${POSTGRES_DB} > backup.sql
\`\`\`
### Redis
**Image**: redis:7-alpine
**Purpose**: Cache and session storage
**Configuration**: ./config/redis/redis.conf
**Secrets**:
- redis_password
**Volumes**:
- redis_data:/data (persistent)
- ./config/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
**Health Check**: `redis-cli ping`
**Backup**:
\`\`\`bash
docker compose exec redis redis-cli SAVE
docker compose cp redis:/data/dump.rdb ./backup/
\`\`\`
## Service Dependencies
\`\`\`
nginx → app → postgres
↓
redis
\`\`\`
## Scaling
To scale the application:
\`\`\`bash
docker compose up -d --scale app=3
\`\`\`
Note: nginx configuration must support multiple backend servers.
docs/decisions/0001-stack-architecture.md
# 1. Stack Architecture
**Date**: YYYY-MM-DD
**Status**: Accepted
**Deciders**: [Names]
## Context
We need a consistent, maintainable approach for deploying our application stack.
## Decision
We will use GitLab Stack Management patterns:
1. All configuration in docker-compose.yml and ./config
2. All secrets in ./secrets and Docker secrets
3. docker-entrypoint.sh only when containers don't support native Docker secrets
4. No root-owned files
5. ./_temporary for transient files
6. Complete validation before deployment
## Consequences
**Positive**:
- Consistent structure across all stacks
- Automated validation prevents deployment issues
- Secure by default (secrets properly managed)
- Easy to maintain and update
- Clear separation of config and secrets
**Negative**:
- Initial setup requires more steps
- Must follow strict patterns
- Validation gates can slow rapid iteration
- Learning curve for team members
## Compliance
Stack creation enforces:
- stack-validator: Structure compliance
- secrets-manager: Secure secrets handling
- docker-validation: Docker best practices
- git hooks: Pre-commit validation
## Alternatives Considered
1. **Manual setup**: Rejected due to inconsistency
2. **.env for everything**: Rejected due to security concerns
3. **No validation**: Rejected due to quality issues
## Implementation
- Use stack-creator skill for all new projects
- Validate with ./scripts/validate-stack.sh
- Document all deviations in new ADRs
Git Configuration Templates
.gitignore
# Secrets - NEVER commit
secrets/*
!secrets/.gitkeep
!secrets/README.md
*.key
*.pem
*.crt
*.p12
*.pfx
id_rsa
id_ed25519
# Environment files
.env
.env.local
.env.*.local
!.env.example
# Temporary files
_temporary/
*.tmp
*.temp
.cache/
*.log
# Docker
.docker/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
Thumbs.db
# Backup files
*.bak
*.backup
*.old
# Build artifacts
dist/
build/
node_modules/
.dockerignore
.git
.gitignore
.github
.gitlab-ci.yml
README.md
LICENSE
docs/
_temporary/
*.md
.env
.env.example
.env.local
secrets/
.vscode/
.idea/
*.swp
*.swo
*.log
*.tmp
node_modules/
.DS_Store
Thumbs.db
CLAUDE.md Template
# CLAUDE.md
This file provides guidance to Claude Code when working with this GitLab Stack project.
## Project Type
This is a GitLab Stack project following strict management patterns.
## Directory Structure
- `config/`: Service configurations (nginx, postgres, redis)
- `secrets/`: Docker secrets (NEVER commit actual secrets!)
- `_temporary/`: Temporary files (gitignored)
- `scripts/`: Validation and utility scripts
- `docs/`: Project documentation
## Required Skills
This project requires these Claude Code skills:
- **stack-validator**: Validate structure
- **secrets-manager**: Manage Docker secrets
- **docker-validation**: Validate Docker configs
- **config-generator**: Generate service configs
## Git Configuration
- **Branch**: main
- **Merge Strategy**: ff-only (fast-forward only)
- **Hooks**: Pre-commit validation enabled
## Validation Requirements
BEFORE any commit, ALL must pass:
1. stack-validator: NO issues
2. secrets-manager: Satisfied
3. docker-validation: NO issues
4. No root-owned files
5. No secrets in .env or docker-compose.yml environment
## Making Changes
### Adding a Service
1. Update docker-compose.yml
2. Use config-generator for configs
3. Use secrets-manager for secrets
4. Run ./scripts/validate-stack.sh
5. Fix ALL issues
6. Commit
### Modifying Configuration
1. Edit config files
2. Validate with docker-validation
3. Run ./scripts/validate-stack.sh
4. Fix issues
5. Commit
### Working with Secrets
1. Use secrets-manager for ALL secret operations
2. NEVER put secrets in .env
3. Use Docker secrets or ./secrets/
4. Validate before committing
## Important Rules
1. NEVER commit secrets
2. NEVER create root-owned files
3. NEVER skip validation without asking
4. NEVER use workarounds - ask user
5. ALWAYS validate before committing
6. ALWAYS document decisions
7. ALWAYS use ff-only merges
8. ALWAYS use main as branch name
## Troubleshooting
- Validation fails → Fix issues, don't skip
- Git conflicts → Use ff-only, ask user
- Permission issues → Check ownership, fix with chown
Summary
All templates follow GitLab Stack Management principles:
- ✅ Secrets in Docker secrets, never in .env
- ✅ Configuration in ./config directory
- ✅ Validated before deployment
- ✅ Documented thoroughly
- ✅ Git properly configured
Use these templates as starting points and customize for your specific needs.