- Add Docker daemon configuration to use HTTP for git.michaelschiemer.de:5000 registry
- Configure insecure-registries in /etc/docker/daemon.json
- Add GIT_BRANCH environment variable (staging for staging, main for production)
- Set default GIT_REPOSITORY_URL if not provided
- Fixes 'http: server gave HTTP response to HTTPS client' error
- Fixes missing GIT_BRANCH variable warnings
- Change registry_accessible to string comparison ('true'/'false') instead of bool
- Fix 'argument of type bool is not iterable' error in when conditions
- Set correct owner/group for .env file (ansible_user instead of root)
- Fixes 'permission denied' error when docker compose reads .env file
- Fix 'argument of type bool is not iterable' error in image pull task
- Check if .env file exists before docker compose up
- Create minimal .env file if it doesn't exist with required variables
- Load secrets from vault file if available
- Set database and MinIO variables from vault or defaults
- Pass environment variables to docker compose command
- Fixes missing MINIO_ROOT_USER, DB_USERNAME, DB_PASSWORD, SECRETS_DIR errors
- Add registry_accessible flag to safely check registry status
- Fix 'argument of type bool is not iterable' error in when conditions
- Only pull image if registry is accessible
- Add ignore_errors to image pull task to prevent failures
- Improves handling of registry connectivity issues
- Fix recursive loop in app_name variable
- Set app_name and deploy_image using set_fact tasks
- Replace application_stack_dest with application_code_dest (consistent with other playbooks)
- Change registry URL from HTTP to HTTPS
- Add validate_certs: no for registry accessibility check
- Fixes 'Recursive loop detected' error in image deployment
- Move deploy-image.yml before install-composer-dependencies.yml
- Containers must be running before composer can install dependencies
- Fixes 'container not running' error in composer install step
- Applied to both staging and production workflows
- Remove container start logic - containers should be started by deploy-image.yml
- Add clear error message if container is not running
- Provides helpful instructions for manual container start if needed
- Check if container is running before executing composer
- Start container if not running
- Display detailed error output for debugging
- Fixes composer install failures when container is not running
- Change from stacks path to application code directory (/home/deploy/michaelschiemer/current)
- docker-compose files are in the application root, not in deployment/stacks
- Fixes 'no such file or directory' error for docker-compose.base.yml
- Change stacks_base_path_default from /home/deploy to /home/deploy/deployment/stacks
- Matches actual server directory structure where stacks are located
- Check if destination directory exists separately from git repo check
- Remove directory if it exists but is not a git repository
- Prevents 'destination path already exists' error during clone
- ansible.builtin.git no longer supports owner and group parameters
- Set ownership in separate file task after git operations
- Fixes 'Unsupported parameters' error
- Use git_repo_url instead of git_repository_url in tasks
- Set git_repo_url based on whether git_repository_url is provided
- This completely avoids the recursive loop issue
- Change git_repository_url to use git_repository_url_default instead of self-reference
- Fixes 'Recursive loop detected in template' error in Ansible playbook
- Test commit to verify that workflow can now:
- Use php-ci image with Ansible
- Use ANSIBLE_VAULT_PASSWORD secret for vault decryption
- Successfully deploy to staging
- Change deploy-staging and deploy-production to use php-ci runner
- php-ci image has Ansible pre-installed, fixing 'ansible-playbook: command not found' error
- Test commit to verify that workflow can now:
- Use php-ci image from docker-dind
- Login to registry with configured secrets
- Build and push images successfully
- Add build-ci-image-production.sh script for building CI images on production
- Add BUILD_ON_PRODUCTION.md documentation
- Fix Dockerfile to handle optional PECL extensions for PHP 8.5 RC
This fixes the issue where Gitea workflows fail with:
'Error response from daemon: pull access denied for php-ci'
- Add gitea-service.yml with proper timeout configuration
- Service definition required for Traefik to route to Gitea
- Replaces old gitea.yml file that was removed
- Change Traefik local HTTP port from 8080 to 8081 (conflict with cadvisor)
- Change Traefik dashboard port to 8093 (conflicts with cadvisor, Hyperion)
- Update Gitea SSH service IP from 172.23.0.2 to 172.23.0.3
- Note: Gitea SSH works directly via Docker port mapping in local dev
- Traefik TCP routing only needed for production (host network mode)
- Add TCP entrypoint 'gitea-ssh' on port 2222 in static config
- Create TCP router configuration for routing SSH traffic to Gitea
- Use Gitea container IP (172.23.0.2) since Traefik runs in host network mode
- Routes git.michaelschiemer.de:2222 through Traefik instead of direct VPN access
- Add docker-compose-direct-access.yml for VPN-only admin access
- Configure Portainer on port 9002 (avoid MinIO conflict)
- Add grafana.ini to disable external plugin update checks
- Bind services to 10.8.0.1 (WireGuard VPN gateway)
This configuration enables direct access to admin services via WireGuard VPN
while removing Traefik routing overhead. Services are bound exclusively to
the VPN gateway IP to prevent public access.
The Redis container was failing with 'Permission denied' when trying to create
the appendonlydir for AOF (Append-Only File) persistence. The error occurred because:
1. Redis runs as root to read Docker Secrets from /run/secrets/redis_password
2. The /data volume is owned by UID 999 (default redis user)
3. cap_drop: ALL removed the CHOWN capability needed to create subdirectories
4. AOF persistence requires creating appendonlydir in /data with proper ownership
Solution:
- Added CHOWN capability: Allows Redis to create directories with correct ownership
- Added DAC_OVERRIDE capability: Allows writing to volume owned by different user
- Maintains all other security restrictions (no-new-privileges, minimal capabilities)
This fixes the continuous restart loop that persisted through commits:
- 5f7ebd9: Fixed healthcheck variable syntax
- 700fe81: Fixed entrypoint script variables
- bfe6a96: Changed healthcheck to read secret directly
The real issue was not the healthcheck but the permission error that prevented
Redis from starting in the first place.
Refs: Redis container logs showed:
'Can't open or create append-only dir appendonlydir: Permission denied'
The health check now reads the password directly from /run/secrets/redis_password
instead of relying on an environment variable, which is not available in the
health check context.
This resolves the 'container application-redis-1 is unhealthy' error.
Previous fix (5f7ebd9) only updated health check line but missed entrypoint script.
The entrypoint script was still using $$REDIS_PASSWORD (Docker Compose escaping)
instead of $REDIS_PASSWORD (shell variable syntax).
Changes:
- Line 180: export REDIS_PASSWORD=$(cat ...) - now uses single $
- Line 182: if [ -n "$REDIS_PASSWORD" ] - now uses single $
- Line 190: --requirepass "$REDIS_PASSWORD" - now uses single $
Technical explanation:
The command: block is a multi-line shell script passed to /bin/sh -c.
Within this shell script context, we use normal shell variable syntax with
single $ for variable references. The export statement makes REDIS_PASSWORD
available to both the Redis process and the health check command.
This completes the fix for: "container application-redis-1 is unhealthy"
Related: 5f7ebd9 (health check fix), b1e3a00 (fallback strategy)
- Changed health check from $$REDIS_PASSWORD to $REDIS_PASSWORD
- Double dollar sign is Docker Compose variable escaping (wrong context)
- Single dollar sign correctly references environment variable exported by entrypoint
- Health check runs in container shell where REDIS_PASSWORD is available
- Fixes 'container application-redis-1 is unhealthy' deployment failure
Changes:
- Export REDIS_PASSWORD from Docker Secret in entrypoint script
- Health check now uses exported environment variable instead of reading Secret file
- Increased start_period to 30s to allow more time for initialization
Why this works:
- Environment variables are accessible to both main process and health checks
- Docker Secret file reading in health check context was unreliable
- Export makes password available in same shell session for health check
Security:
- Password still sourced from Docker Secret (encrypted at rest)
- Only exported within container environment (not exposed externally)
- Redis still requires password authentication (--requirepass)
Deployment fix#11 (continued): Redis container health check
Changed health check to try without password first, then with Docker Secret.
This handles both scenarios where password might not be immediately available
or where the Secret read might fail in health check context.
Changes:
- Use CMD-SHELL instead of CMD for shell expansion support
- Try 'redis-cli ping' first (no auth)
- Fallback to authenticated ping if first attempt fails
- Properly quote password from Docker Secret
This is the eleventh cumulative fix for production deployment pipeline.
Related: commit 477fe67 (initial Redis health check fix)