AI agent operating manual for BeeCompose. Read completely before any action.
BeeCompose – Production-ready Docker Compose stacks. Curated.
- Created: April 2020
- Purpose: Docker-Compose configurations for self-hosted services
- Distribution: OCI artifacts published to GitHub Container Registry
- Reverse Proxy: Traefik v3 with Let's Encrypt (CloudFlare DNS-01) or Cloudflare Tunnel
- Linting: DCLint (zavoloklom/docker-compose-linter) for compose file validation
- Follow existing docker-compose patterns exactly
- Use
name: <service>to explicitly set the project name (do NOT useversion- it is obsolete) - Include
restart: unless-stoppedon all containers - Add JSON logging limits to every service:
logging: driver: "json-file" options: max-size: "500k" max-file: "50"
- Connect services to Traefik via external network
traefik_default - Use environment variable substitution (
${VAR}) in compose files - Place service-specific configs in the service directory
- Embed version tags as default values directly in docker-compose.yml (e.g.,
image: nginx:${NGINX_VERSION:-1.25.3}) for OCI compatibility - Use
.envfor version tags,.env.examplefor configuration templates - Use
example.comas placeholder domain in examples - Use
Swordfishas placeholder password in examples - Pin image versions explicitly (see Image Tagging Policy below)
- Use explicit interface binding for ports (e.g.,
"0.0.0.0:80:80") - Run DCLint before submitting compose file changes
- Never execute docker-compose commands locally (
docker-compose up,docker-compose down,docker-compose pull, etc.) - Never commit real secrets, credentials, or API keys
- Never run destructive commands (
down --volumes) without explicit approval - Never run git write operations (
git commit,git push,git merge)
Pin image versions explicitly to ensure reproducible deployments.
Standard Rule: Use explicit version tags (e.g., nginx:1.25.3, postgres:15-alpine)
Allowed Exceptions:
- Redis: May use
latesttag (stable, backward-compatible) - Images with only
latestavailable: Document with comment in docker-compose.yml explaining the exception
Example for exception:
# docker-compose.yml
# Note: redash/nginx only publishes 'latest' tag - no versioned tags available
image: redash/nginx:${REDASH_NGINX_VERSION:-latest}Docker Compose files are validated using DCLint.
# Validate all compose files (read-only)
docker run --rm -v "$(pwd):/app" zavoloklom/dclint:latest /app/services -r -c /app/.dclintrc.yaml
# Auto-fix style issues (formatting, ordering)
docker run --rm -v "$(pwd):/app" zavoloklom/dclint:latest /app/services -r -c /app/.dclintrc.yaml --fixConfiguration is in .dclintrc.yaml. Key rules enforced:
| Rule | Level | Description |
|---|---|---|
no-quotes-in-volumes |
Error | Volume paths must not be quoted |
require-quotes-in-ports |
Error | Port mappings must use double quotes |
service-image-require-explicit-tag |
Error | Images must have explicit tags |
no-duplicate-container-names |
Error | Container names must be unique |
no-duplicate-exported-ports |
Error | Exported ports must be unique |
require-project-name-field |
Error | Compose files must have name field |
no-unbound-port-interfaces |
Error | Ports must specify interface (0.0.0.0) |
service-keys-order |
Warning | Service keys should follow standard order |
services-alphabetical-order |
Warning | Services should be alphabetically sorted |
The linter runs automatically in the CI/CD pipeline (Job 1). All errors must be fixed before merging.
beecompose/
├── .dclintrc.yaml # Docker Compose linter configuration
├── .github/
│ ├── workflows/ # CI/CD pipelines
│ └── scripts/ # Test and validation scripts
├── docs/
│ ├── BACKUP.md # Backup procedures
│ ├── DEPLOYMENT.md # Deployment guide
│ └── TESTING.md # Testing procedures
├── scripts/
│ ├── bc # CLI helper (optional, install system-wide)
│ ├── install.sh # bc CLI installer
│ ├── publish-dry-run.sh # OCI publishing dry run
│ ├── test-oci.sh # Test OCI deployment
│ └── validate-all-oci.sh # Validate OCI compatibility
└── services/
└── <service>/
├── docker-compose.yml # Compose configuration (versions as defaults)
├── README.md # Service documentation
├── .env # Version tags (committed)
├── .env.example # Example config (committed)
└── .env.<environ> # Environment secrets (gitignored)
The bc CLI simplifies OCI artifact deployment. Usage is optional - direct docker compose commands work equally well.
# Install bc CLI (optional)
curl -fsSL https://raw.githubusercontent.com/beevelop/beecompose/main/scripts/install.sh | sudo bash
# Deploy service
bc <service> up # Starts service, always pulls latest
bc <service> down # Stops service
bc <service> logs -f # Follow logs
bc <service> ps # Check status
bc <service> update # Pull and recreate
# Pin OCI version
bc init v26.1.6 # Creates .beecompose config
bc -v latest sentry up # Override version per-commandThe bc CLI:
- Wraps
docker compose -f oci://...with opinionated defaults - Always pulls latest images (
--pull always) - Automatically uses
.env.<service>for environment variables - Supports version pinning via
.beecomposefile or-vflag
Note: When documenting services, always show both bc CLI and manual docker compose commands.
# Deploy directly from GHCR
docker compose -f oci://ghcr.io/beevelop/<service>:latest --env-file .env up -d
# View logs
docker compose -f oci://ghcr.io/beevelop/<service>:latest --env-file .env logs -f
# Stop service
docker compose -f oci://ghcr.io/beevelop/<service>:latest --env-file .env downcd services/<service>
# Deploy locally
docker compose --env-file .env.example up -d
# View logs
docker compose logs -f
# Stop
docker compose down# Validate single service for OCI compatibility
./scripts/publish-dry-run.sh <service>
# Validate all services
./scripts/validate-all-oci.sh
# Test OCI artifact deployment
./scripts/test-oci.sh <service> [version]Allowed without asking:
- Read files, list directories
- Analyze docker-compose configurations
- Validate YAML syntax
- Create new
.env.examplefiles - Run DCLint in read-only mode (validation only)
- Apply DCLint auto-fixes for style issues (formatting, key ordering)
Ask first:
- Modifying existing docker-compose.yml files (beyond lint fixes)
- Adding new services
- Modifying CI/CD workflows
Never do (require explicit confirmation):
- Execute any docker-compose commands locally
- Any git write operations
Service keys must follow DCLint's expected order:
name: <service>
services:
<service>:
image: <image>:${<SERVICE>_VERSION:-1.2.3}
container_name: <service>
depends_on: [dependency1, dependency2]
volumes:
- <service>_data:/container/path
environment:
- VAR=${VAR}
ports:
- "0.0.0.0:8080:8080"
networks: [<service>, traefik]
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "500k"
max-file: "50"
labels:
- "traefik.enable=true"
- "traefik.http.routers.<service>.rule=Host(`${SERVICE_DOMAIN}`)"
- "traefik.http.routers.<service>.entrypoints=websecure"
- "traefik.http.services.<service>.loadbalancer.server.port=<port>"
- "traefik.docker.network=traefik_default"
volumes:
<service>_data:
networks:
<service>:
traefik:
external:
name: traefik_defaultTLS Note: Do NOT include
tls=trueortls.certresolverlabels. TLS is configured at the Traefik entrypoint level, enabling services to work in both exposed (Let's Encrypt) and tunnel (Cloudflare) modes without modification.
OCI Note: Use named volumes (not bind mounts) for OCI artifact compatibility.
When defining services, use this key order for consistency:
imagebuildcontainer_namedepends_onvolumesvolumes_fromconfigssecretsenvironmentenv_fileportsnetworksnetwork_modeextra_hostscommandentrypointworking_dirrestarthealthchecklogginglabelsuserisolation
.env (committed) - Version tags only:
SERVICE_VERSION=1.2.3
POSTGRES_TAG=17-alpine.env.example (committed) - Configuration template:
SERVICE_DOMAIN=service.example.com
DB_USER=bee
DB_PASS=Swordfish
SECRET_KEY=your_secret_hereAll services use Traefik v3 label syntax for routing configuration. TLS is handled at the entrypoint level, NOT in service labels:
| Label | Purpose |
|---|---|
traefik.enable=true |
Enable Traefik for this container |
traefik.http.routers.<name>.rule=Host(\domain`)` |
Route by hostname (use backticks!) |
traefik.http.routers.<name>.entrypoints=websecure |
Use HTTPS entrypoint |
traefik.http.services.<name>.loadbalancer.server.port=<port> |
Container port to route to |
traefik.docker.network=traefik_default |
Docker network for routing |
Important:
- The
<name>in router/service labels should match the service/container name for consistency - Do NOT use
tls=trueortls.certresolverlabels - TLS is configured at the Traefik entrypoint level to support both exposed (Let's Encrypt) and tunnel (Cloudflare) modes
<Service>: <Description>
# Examples:
GitLab: 13.5.3
MetaBase: v0.36.4
Sentry: Update base image to getsentry/sentry
- Use service name as prefix
- Include version number for upgrades
- Reference issues with
(close #123)when applicable
When adding a new service, you MUST update multiple files to ensure full coverage. The CI/CD pipeline will fail if any required file is missing the new service.
- Create
services/<name>/directory - Create
docker-compose.ymlfollowing the standard template (with version defaults embedded) - Create
.envwith version tags - Create
.env.examplewith placeholder secrets - Create
README.mdwith deployment instructions - Use named volumes (no bind mounts for OCI compatibility)
- Run DCLint to validate:
docker run --rm -v "$(pwd):/app" zavoloklom/dclint:latest /app/services/<name> -c /app/.dclintrc.yaml - Add to README.md - Add row to the services table (required)
- Add to scripts/bc - Add entry to
cmd_list()function (required)
- Add to
.github/workflows/check-versions.yml- Add entry toIMAGE_REGISTRIESarray for automatic version checking - Add to
.github/dependabot.yml- Add docker-compose ecosystem entry for Dependabot updates
The CI/CD pipeline includes a service coverage check that verifies all services are properly documented. You can run this check locally:
# Check service coverage
./.github/scripts/check-service-coverage.sh
# Show fix hints for missing entries
./.github/scripts/check-service-coverage.sh --fix-hintsWhen adding or removing services, multiple files must stay synchronized. The CI/CD pipeline enforces this automatically.
| File | Location | Purpose |
|---|---|---|
README.md |
Services table (lines 15-49) | User-facing service documentation |
scripts/bc |
cmd_list() function |
CLI helper service listing |
| File | Location | Purpose |
|---|---|---|
.github/workflows/check-versions.yml |
IMAGE_REGISTRIES array |
Automatic upstream version checking |
.github/dependabot.yml |
Per-service docker-compose entries | Dependabot image update PRs |
These files dynamically find services and don't need manual updates:
| File | Method |
|---|---|
.github/workflows/ci-cd.yml |
find services -name "docker-compose.yml" |
.github/workflows/publish-oci.yml |
find services -maxdepth 1 -mindepth 1 -type d |
- Update version in
.envfile - Update version default in
docker-compose.ymlimage tag - Check upstream changelog for breaking changes
- Update
docker-compose.ymlif required - Run DCLint to validate changes
- Update
README.mdif configuration changed - Commit with format:
<Service>: <version>
| Tool | Purpose |
|---|---|
| Docker 25.0+ | Container runtime with OCI support |
| Docker Compose v2.24+ | Service orchestration |
| DCLint | Docker Compose linting (via Docker image) |
- Reference existing services as examples (GitLab, Sentry are comprehensive)
- Run DCLint to validate compose syntax
- Check service README for configuration details
- Run
./scripts/validate-all-oci.shto check OCI compatibility
Keep it simple. Keep it self-hosted. Keep it running.