This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Stacksmith is a modular Docker-based infrastructure management system built around Portainer as the central management interface. It provides a complete self-hosted solution for managing containerized applications with enterprise-grade security, SSL automation, and flexible service composition.
- Modular Design: Each service is independently deployable and composable
- Security-First: Integrated with Tailscale VPN and enterprise OAuth
- Flexible Deployment: Services can be deployed where operationally optimal
- Self-Hosted Focus: Complete infrastructure stack without external dependencies
- Portainer (
/docker-compose.yml) - Docker management web interface - Traefik (
/traefik/) - Reverse proxy with automatic HTTPS
Additional services are available in their respective directories, each with comprehensive documentation in their individual README.md files.
All services follow a consistent pattern:
# NOTE: The 'version' field has been deprecated and should not be used
services:
service-name:
image: service/image:latest
container_name: stacksmith_servicename
restart: unless-stopped
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- service-data:/data
networks:
- stacksmith
labels:
- "traefik.enable=true"
- "traefik.http.routers.service.rule=Host(`${SERVICE_HOSTNAME}`)"
- "traefik.http.routers.service.entrypoints=websecure-tailscale"
- "traefik.http.routers.service.tls.certresolver=stacksmith"
- "traefik.http.services.service.loadbalancer.server.port=PORT"
- "traefik.http.routers.service.middlewares=secure-headers@docker"- Consistent naming: All containers prefixed with
stacksmith_ - External network: All services join the
stacksmithnetwork - Traefik integration: Automatic service discovery via Docker labels
- Volume management: Named volumes for persistence, bind mounts for config
- User management: Consistent PUID/PGID across LinuxServer images
- Hostname convention: Ultra-short 3-4 character hostnames that abstract the function (e.g.,
dnsfor Pi-hole,mgmtfor Portainer) - Subdomain routing: Prefer subdomains over complex path routing for services with multiple endpoints (e.g.,
api.service.example.cominstead ofservice.example.com/api)
- Primary Access: Services bound to Tailscale IP (100.64.0.1)
- Secure Overlay: All services accessible via Tailscale VPN
- Dual Entrypoints: Primary (Tailscale) and secondary interfaces
# Entrypoint Configuration (internal container ports)
- --entrypoints.web-tailscale.address=:80
- --entrypoints.websecure-tailscale.address=:443
- --entrypoints.web-secondary.address=:8080
- --entrypoints.websecure-secondary.address=:8443
# Port Mappings (external_ip:external_port:internal_port)
ports:
- "${TRAEFIK_TAILSCALE_IP}:80:80"
- "${TRAEFIK_TAILSCALE_IP}:443:443"
- "${TRAEFIK_SECONDARY_IP}:80:8080"
- "${TRAEFIK_SECONDARY_IP}:443:8443"- Let's Encrypt: Automatic certificate generation
- Cloudflare DNS Challenge: Wildcard certificate support
- Strong TLS: Custom configuration in
/traefik/dynamic/tls.yml
- Main Environment (
/.env.example): Core infrastructure settings - Service-specific (
/service/.env.example): Individual service configuration
# Core Infrastructure
PORTAINER_HOSTNAME=mgmt.example.com
TRAEFIK_HOSTNAME=prxy.example.com
TRAEFIK_TAILSCALE_IP=100.64.0.1
TRAEFIK_SECONDARY_IP=127.0.0.1
# SSL Configuration
ACME_EMAIL=your-email@example.com
CLOUDFLARE_DNS_API_TOKEN=your-cloudflare-api-token
# Timezone Configuration
# IMPORTANT: Always use Europe/Zurich as the default timezone in .env.example files
TZ=Europe/Zurich# Create external network (required first step)
docker network create stacksmithdocker compose -f docker-compose.yml -f traefik/docker-compose.yml up -d# Deploy any additional service
docker compose -f traefik/docker-compose.yml -f servicename/docker-compose.yml up -d
# Deploy multiple services together
docker compose -f docker-compose.yml -f traefik/docker-compose.yml -f service1/docker-compose.yml -f service2/docker-compose.yml up -d- JumpCloud OAuth: Optional for Portainer only (configuration in
/PORTAINER_OAUTH_SETUP.md) - Tailscale VPN: Primary access method for all services
- Service-level Authentication: Individual service security
- Tailscale VPN: Primary access method
- Network Isolation: Services communicate through dedicated network
- HTTPS Enforcement: Automatic HTTP to HTTPS redirect
- No Default Passwords: Secure defaults across all services
- API Token Management: Scoped tokens for external services
- Certificate Automation: Automated SSL certificate management
- Docker Socket Access: Full Docker management capabilities
- OAuth Integration: Post-deployment JumpCloud configuration
- Volume Persistence: Configuration stored in named volume
- Dynamic Configuration: File provider for custom configurations
- Service Discovery: Automatic Docker service detection
- Load Balancing: Built-in load balancing capabilities
- Middleware Support: Authentication, rate limiting, headers
# Always create the external network first (only needed once)
docker network create stacksmith# Deploy specific service
docker compose -f servicename/docker-compose.yml up -d
# Deploy multiple services together
docker compose -f docker-compose.yml -f traefik/docker-compose.yml up -d
# View service logs
docker compose logs -f servicename
# Update service
docker compose pull servicename
docker compose up -d servicename
# Stop services
docker compose down
# Remove services and volumes (destructive)
docker compose down -v# Copy environment files for new services
cp .env.example .env
cp servicename/.env.example servicename/.env
# Edit environment files with service-specific values
# Required: Set hostnames, IPs, and tokens# Backup named volume
docker run --rm -v volumename:/data -v $(pwd):/backup alpine tar czf /backup/backup.tar.gz /data
# Restore named volume
docker run --rm -v volumename:/data -v $(pwd):/backup alpine tar xzf /backup/backup.tar.gz -C /- Flat Organization: Each service in its own directory
- Independent Services: Services can be developed and deployed independently
- Consistent Patterns: Follow established patterns for new services
- Create service directory:
mkdir servicename - Create
docker-compose.ymlfollowing the standard pattern - ALWAYS create
.env.examplewith service-specific variables - ALWAYS create
README.mdwith comprehensive documentation - Follow Traefik integration patterns:
- Use
websecure-tailscaleentrypoint (orwebsecure-secondaryif needed) - Include required Traefik labels
- Set correct service port
- Use
- Use consistent naming:
stacksmith_servicename - Join
stacksmithexternal network - Include standard environment variables (PUID, PGID, TZ) when applicable
- CRITICAL: Every service MUST have both
.env.exampleandREADME.mdfiles - Keep Documentation Updated: When modifying services, update both files accordingly
- Environment Variables: Document all variables in both
.env.exampleandREADME.md - Deployment Instructions: Include complete deployment steps in README
- Troubleshooting: Add common issues and solutions to README
- No Production Secrets: Use
.env.exampletemplates only - Clear Documentation: Document all environment variables
- Secure Defaults: Provide secure default configurations
- Consistency: Follow the same .env.example format across all services
- Single Management Interface: Portainer provides unified container management
- Flexible Deployment: Services can be deployed across multiple environments
- Enterprise Security: Integrated VPN and OAuth authentication
- Automated SSL: Let's Encrypt with DNS challenge automation
- Modular Architecture: Add/remove services without affecting others
- Standard Images: Uses official and well-maintained container images
- No Custom Builds: Pure Docker Compose without complex build processes
- Scalable Design: Supports multi-environment and multi-host deployments
- VPN-First Access: Tailscale integration for secure remote access
- Automated Certificates: Let's Encrypt with Cloudflare DNS challenges
- Network Isolation: Services communicate through dedicated networks
- Enterprise Authentication: JumpCloud OAuth integration
This repository represents a mature, production-ready Docker infrastructure system suitable for personal use, home labs, or small to medium business deployments.
For testing changes before committing, replicate the production environment locally:
- Setup: Ensure
*.dev.example.comresolves to127.0.0.1via Cloudflare DNS - Environment: Set
TRAEFIK_TAILSCALE_IP=127.0.0.1in local.envfiles - Deploy: Use standard Docker Compose commands with local domains
- Test: Verify service accessibility and SSL certificate generation
- Prompt user for their actual test domain (don't read .env files)
- Use the domain from user's
~/.claude/CLAUDE.mdmemory if available - Test complete deployment workflow including Traefik routing
Do what has been asked; nothing more, nothing less.
NEVER ever read .env files as contained secrets would leak into your logs/telemetry at Anthropic.
NEVER create files unless they're absolutely necessary for achieving your goal.
ALWAYS prefer editing an existing file to creating a new one.
NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.