Skip to content

Immutable Files Architecture - Implementation Complete ✅

⚠️ Historical doc. Describes the 2025-01 deploy-script architecture that wrapped infrastructure/scripts/ci-deploy.sh. That script and the make deploy-{shared,qual,prod} targets were retired in Riff #50 (2026-04-30). Current deploy doctrine: GitLab CI auto-deploys qual on push to main; prod is a manual CI job (Riff #10). See docs/reference/makefile.md for the current Makefile surface.

Summary

All compose files and .env files are now immutable - they are never modified by scripts or CI/CD processes. All customization happens via environment variables passed at runtime.

Changes Made

1. CI/CD Refactored ✅

File: .gitlab-ci/infrastructure.yml

Before:

# ❌ Modified compose file
bash infrastructure/scripts/update-image-tags.sh qual "${IMAGE_TAG}"
docker compose -f qualification.yml up -d

After:

# ✅ Uses environment variables
export IMAGE_TAG="${CI_COMMIT_SHORT_SHA}"
IMAGE_TAG="${IMAGE_TAG}" docker compose -f qualification.yml --env-file .env.qual up -d

Result: Compose files are never modified by CI/CD.

2. Scripts Updated ✅

  • update-image-tags.sh:
  • Marked as DEPRECATED
  • Only works in CI/CD (safety check prevents manual use)
  • Should not be used - use environment variables instead

  • ci-deploy.sh:

  • Already uses environment variables correctly ✅
  • No changes needed

  • generate-secrets.sh:

  • ✅ OK - Generates files from templates (doesn't modify existing)
  • This is the intended way to create .env files

3. Documentation Created ✅

  • docs/devops/deployment/immutable-files-architecture.md - Complete architecture guide
  • infrastructure/compose/README.md - Compose files documentation
  • docs/devops/deployment/fix-image-tag-issue.md - Troubleshooting guide

Architecture Principles

Compose Files: IMMUTABLE ✅

Status: Never modified by any script or CI/CD

How they work:

image: ${REGISTRY_IMAGE:-registry.gitlab.com/group/project}/service:${IMAGE_TAG:-latest}

Variables read from: 1. .env files (via env_file directive) 2. Environment variables (passed at runtime, takes precedence)

Runtime override:

IMAGE_TAG=abc123 docker compose -f qualification.yml --env-file .env.qual up -d

.env Files: GENERATED ✅

Status: Generated from templates, never modified after generation

Generation: - Created from templates in infrastructure/env-templates/ - Generated by generate-secrets.sh script - CI/CD generates fresh files from templates

After generation: - Files are never modified by scripts - Manual edits allowed (for external API keys like Stripe, AWS) - CI/CD generates fresh files (doesn't modify existing)

Customization: ENVIRONMENT VARIABLES ✅

All customization via environment variables at runtime:

CI/CD:

export IMAGE_TAG="${CI_COMMIT_SHORT_SHA}"
IMAGE_TAG="${IMAGE_TAG}" docker compose -f qualification.yml --env-file .env.qual up -d

Manual:

# .env.qual has IMAGE_TAG=latest (locked)
docker compose -f qualification.yml --env-file .env.qual up -d

Verification

✅ CI/CD Doesn't Modify Files

grep -c "update-image-tags" .gitlab-ci/infrastructure.yml
# Output: 0 (no modifications)

✅ Compose Files Use Variables

grep "IMAGE_TAG" infrastructure/compose/qualification.yml | head -1
# Output: image: .../service:${IMAGE_TAG:-latest}

✅ .env Files Are Generated

ls -la infrastructure/compose/.env.*
# Shows generated files from templates

File Status

✅ Immutable (Never Modified)

  • infrastructure/compose/*.yml - All compose files
  • .env.qual, .env.prod - After generation, only manual edits allowed

✅ Generated (From Templates)

  • infrastructure/compose/.env.shared - From env.shared.template
  • infrastructure/compose/.env.qualification - From env.qualification.template
  • infrastructure/compose/.env.production - From env.production.template

❌ Deprecated (Don't Use)

  • update-image-tags.sh - Use environment variables instead

Usage Examples

Manual Deployment

# 1. Ensure .env.qual has IMAGE_TAG=latest
grep IMAGE_TAG .env.qual

# 2. Deploy (compose file reads from .env.qual)
docker compose -f qualification.yml --env-file .env.qual up -d

# 3. Optional: Override at runtime
IMAGE_TAG=custom-tag docker compose -f qualification.yml --env-file .env.qual up -d

CI/CD Deployment

# CI/CD automatically:
# 1. Exports IMAGE_TAG="${CI_COMMIT_SHORT_SHA}"
# 2. Passes to docker compose
# 3. Compose file uses environment variable (overrides .env file)

Benefits

  1. Predictable: Compose files never change unexpectedly
  2. Version Controlled: Compose files stay in sync with git
  3. Flexible: Easy to override with environment variables
  4. Maintainable: Clear separation between definition (compose) and configuration (.env)
  5. Safe: No risk of scripts corrupting compose files

Migration Notes

If you have existing deployments:

  1. Restore compose files from git:

    git checkout infrastructure/compose/qualification.yml
    git checkout infrastructure/compose/production.yml
    

  2. Ensure .env files have IMAGE_TAG:

    # .env.qual should have:
    IMAGE_TAG=latest
    

  3. Deploy using environment variables:

    docker compose -f qualification.yml --env-file .env.qual up -d
    

See Also