# Terraform Docker Renovate Module ## Purpose This Terraform module deploys a Renovate bot as a Docker container with the following features: - **Renovate Container**: Automatically updates dependencies in your repositories - **Gitea Integration**: Native support for Gitea platform with proper authentication - **Traefik Integration**: Automatically configures Traefik reverse proxy (optional) - **DNS Management**: Creates DNS CNAME records for the Renovate instance (optional) - **Persistent Storage**: Manages Docker volumes for configuration and cache - **Vault Integration**: Securely retrieves DNS credentials from HashiCorp Vault - **Remote State Backend**: Stores Terraform state in MinIO (S3-compatible storage) - **Resource Limits**: Configurable memory limits for container isolation ## What It Does The module creates and manages the following resources: 1. **Docker Volumes**: - `renovate-config`: Persistent storage for configuration files - `renovate-cache`: Cache storage for improved performance 2. **Renovate Container**: - Runs Renovate bot process - Connects to Traefik network for reverse proxy access (optional) - Configured with resource limits (Memory) - Configured with flexible restart policy - Logs sent to Docker daemon (managed by Loki) 3. **DNS Record** (Optional): - Creates a CNAME record pointing to the hosting server ## What is Renovate? Renovate is an automated dependency update tool that: - **Monitors dependencies** across multiple package managers and platforms - **Creates pull requests** with dependency updates automatically - **Supports semantic versioning** and custom update schedules - **Works with Docker**, Terraform, npm, pip, and many other ecosystems - **Integrates with CI/CD** pipelines for automated testing ### Common Use Cases - Automated Docker image updates with semantic versioning - Terraform module and provider version updates - Application dependency management (npm, pip, composer, etc.) - Security vulnerability patching through automatic updates - Consistent dependency versions across multiple repositories ## Prerequisites Before using this module, ensure you have: 1. **Docker Host**: A Docker daemon accessible via TCP (configured at `192.168.2.170:2376`) 2. **Docker TLS Certificates**: Client certificates in `~/.docker/` directory 3. **Traefik Network**: A Docker network named `traefik_network` must exist 4. **Gitea Instance**: Running Gitea instance with API access 5. **Renovate Bot User**: Dedicated user account in Gitea with appropriate permissions 6. **Gitea Access Token**: Personal Access Token with the following scopes: - `repo`: Read and Write - `user`: Read - `issue`: Read and Write (Gitea ≥ 1.20.0) - `organization`: Read (Gitea ≥ 1.20.0) 7. **HashiCorp Vault**: Running instance with: - AppRole authentication enabled - DNS credentials stored at `secret/dns` - Renovate credentials stored at `secret/renovate` (see Vault Setup section) - Role ID and Secret ID for authentication 8. **MinIO/S3 Backend**: For Terraform state storage 9. **DNS Server**: Supporting dynamic updates (TSIG authentication) - optional ## Gitea Bot Setup ### 1. Create Renovate Bot User In your Gitea instance: 1. Create a new user account (e.g., `renovate-bot`) 2. Configure the user with: - Full name: "Renovate Bot" - Email: `renovate-bot@example.com` 3. Add the bot user as a collaborator to repositories you want to manage ### 2. Generate Personal Access Token 1. Log in as the Renovate bot user 2. Go to Settings → Applications → Generate New Token 3. Token name: "Renovate Token" 4. Select scopes: - `repo` (Read and Write) - `user` (Read) - `issue` (Read and Write) - `organization` (Read) 5. **IMPORTANT**: Copy the token immediately - you won't be able to see it again 6. Save the token securely - you'll need it for Vault storage (next step) ### 3. Store Credentials in Vault **CRITICAL**: All Renovate configuration must be stored in Vault at `secret/renovate` before deploying this module. ```bash # Authenticate to Vault export VAULT_ADDR="https://your-vault-server:8200" vault login -method=approle role_id=YOUR_ROLE_ID secret_id=YOUR_SECRET_ID # Store Renovate credentials in Vault vault kv put secret/renovate \ renovate_platform="gitea" \ renovate_endpoint="https://gitea.example.com/api/v1/" \ renovate_token="YOUR_GITEA_TOKEN_FROM_STEP_2" \ renovate_git_author="Renovate Bot " \ renovate_username="renovate-bot" # Verify the secrets are stored correctly vault kv get secret/renovate ``` **Required Keys in `secret/renovate`:** - `renovate_platform` - Must be "gitea" - `renovate_endpoint` - Your Gitea API endpoint (must end with `/api/v1/`) - `renovate_token` - The Personal Access Token from Step 2 - `renovate_git_author` - Git commit author for Renovate PRs - `renovate_username` - The Gitea username of the bot account **Note**: If you regenerate the Gitea token, you must update it in Vault: ```bash vault kv patch secret/renovate renovate_token="NEW_TOKEN_HERE" ``` ### 4. Configure Repository Access For each repository you want Renovate to manage: 1. Add `renovate-bot` as a collaborator with Write access 2. Or use autodiscovery to automatically find all accessible repositories ## How to Use ### 1. Basic Usage with Gitea ```hcl module "renovate" { source = "./terraform-docker-renovate" # Infrastructure domain = "bsdserver.nl" role_id = var.vault_role_id secret_id = var.vault_secret_id # Gitea Configuration renovate_platform = "gitea" renovate_endpoint = "https://gitea.example.com/api/v1/" renovate_token = var.renovate_token # Store securely in Vault or use env var renovate_git_author = "Renovate Bot " renovate_username = "renovate-bot" } ``` ### 2. Custom Configuration ```hcl module "renovate" { source = "./terraform-docker-renovate" # Infrastructure domain = "bsdserver.nl" role_id = var.vault_role_id secret_id = var.vault_secret_id # Container configuration container_name = "renovate" renovate_image = "renovate/renovate:latest" restart_policy = "unless-stopped" # Resource limits memory_limit = 2048 memory_swap_limit = -1 # Gitea platform configuration renovate_platform = "gitea" renovate_endpoint = "https://gitea.bsdserver.nl/api/v1/" renovate_token = var.renovate_token renovate_git_author = "Renovate Bot " renovate_username = "renovate-bot" renovate_autodiscover = true # Optional GitHub.com token for changelogs github_com_token = var.github_token # Logging log_level = "info" # Additional environment variables extra_env_vars = [ "RENOVATE_REQUIRE_CONFIG=optional" ] } ``` ### 3. Configure Backend (Optional) If using remote state storage (recommended), configure the backend: ```bash # Option 1: Using environment variables export AWS_ACCESS_KEY_ID="your-minio-access-key" export AWS_SECRET_ACCESS_KEY="your-minio-secret-key" terraform init \ -backend-config="endpoints={s3=\"https://minio.example.com:443\"}" \ -backend-config="bucket=terraform-state" \ -backend-config="key=docker/renovate/terraform.tfstate" \ -backend-config="region=main" \ -backend-config="skip_credentials_validation=true" \ -backend-config="skip_metadata_api_check=true" \ -backend-config="skip_requesting_account_id=true" \ -backend-config="skip_region_validation=true" \ -backend-config="use_path_style=true" ``` Or create a `backend.hcl` file: ```hcl # backend.hcl endpoints = { s3 = "https://minio.example.com:443" } bucket = "terraform-state" key = "docker/renovate/terraform.tfstate" region = "main" skip_credentials_validation = true skip_metadata_api_check = true skip_requesting_account_id = true skip_region_validation = true use_path_style = true ``` Then initialize: ```bash export AWS_ACCESS_KEY_ID="your-minio-access-key" export AWS_SECRET_ACCESS_KEY="your-minio-secret-key" terraform init -backend-config=backend.hcl ``` ### 4. Initialize Terraform ```bash # For local state (not recommended for production) terraform init # Or with remote backend (see step 3) terraform init -backend-config=backend.hcl ``` This will: - Download required providers (Docker, Vault, DNS) - Configure the backend for state storage (if specified) ### 5. Plan Deployment ```bash terraform plan ``` Review the planned changes to ensure everything is correct. ### 6. Apply Configuration ```bash terraform apply ``` Confirm the changes to deploy the Renovate bot. ### 7. Verify Deployment After deployment: 1. **Check Container Status**: ```bash docker ps | grep renovate ``` 2. **View Container Logs**: ```bash docker logs renovate -f ``` 3. **Verify Configuration**: ```bash docker exec renovate cat /usr/src/app/config.js ``` ## Repository Configuration ### Adding Renovate to a Repository To enable Renovate in a repository, create a `renovate.json` file in the repository root: ```json { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:recommended" ], "assignees": ["@yourusername"], "labels": ["renovate"], "dependencyDashboard": true, "packageRules": [ { "description": "Automerge minor and patch updates", "matchUpdateTypes": ["minor", "patch"], "automerge": true } ], "docker": { "enabled": true, "pinDigests": false }, "terraform": { "enabled": true } } ``` An example configuration is provided in `files/example-renovate.json`. ### Docker-Compose Repository Example For repositories with `docker-compose.yml` files: ```json { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "docker-compose": { "enabled": true }, "packageRules": [ { "matchDatasources": ["docker"], "matchUpdateTypes": ["major"], "enabled": false } ] } ``` ### Terraform Repository Example For repositories with Terraform code: ```json { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "terraform": { "enabled": true }, "packageRules": [ { "matchDatasources": ["terraform-provider", "terraform-module"], "automerge": false, "schedule": ["before 6am on Monday"] } ] } ``` ### Docker Container Versioning in Terraform This Renovate deployment includes **regex managers** that can detect and update Docker image versions defined in Terraform files. This is useful when you deploy Docker containers via Terraform and want Renovate to keep those images up to date. #### How It Works Add a special comment annotation above your `image` definition in your Terraform code. Renovate will then detect the Docker image and create PRs when updates are available. #### Annotation Format **Basic annotation** (combined image:tag format): ```hcl locals { containers = { redis = { # renovate: datasource=docker image = "docker.io/library/redis:8" # ... other settings } traefik = { # renovate: datasource=docker image = "traefik:3.1.2" ports = [80, 443] } grafana = { # renovate: datasource=docker image = "grafana/grafana:11.2.0" # ... other settings } } } ``` **With explicit versioning scheme** (for non-standard versioning): ```hcl vault = { # renovate: datasource=docker versioning=semver image = "hashicorp/vault:1.17.3" } minio = { # renovate: datasource=docker versioning=regex image = "minio/minio:RELEASE.2024-08-29T01-40-52Z" } ``` **Separate version variable**: ```hcl # renovate: datasource=docker depName=redis variable "redis_version" { type = string default = "8.0.0" } ``` #### Supported Patterns | Pattern | Example | Use Case | |---------|---------|----------| | Combined image:tag | `image = "nginx:1.25.0"` | Most common - image and version in one string | | With registry prefix | `image = "docker.io/library/redis:8"` | Official images with full path | | Custom versioning | `versioning=semver` | Images with non-standard version formats | | Separate variable | `depName=redis` + `version = "8.0.0"` | When image and version are split | #### Example: Complete Container Definition ```hcl # Paperless-NGX with Redis broker locals { services = { paperless-broker = { # renovate: datasource=docker image = "docker.io/library/redis:8" healthcheck = { test = ["CMD", "redis-cli", "ping"] interval = "30s" timeout = "5s" retries = 3 } volumes = { "paperless-redisdata" = "/data" } networks = ["paperless-backend-network"] container_ports = ["6379"] } paperless-webserver = { # renovate: datasource=docker image = "ghcr.io/paperless-ngx/paperless-ngx:2.12.1" networks = ["paperless-backend-network", "traefik_network"] use_traefik = true container_ports = ["8000"] } } } ``` When Renovate scans this repository, it will: 1. Detect `redis:8` and create PRs for Redis updates 2. Detect `paperless-ngx:2.12.1` and create PRs for Paperless-NGX updates 3. Continue to update Terraform providers and modules as usual An example annotated container file is provided in `files/example-annotated-containers.tf`. ## Variables ### Terraform Variables These variables are defined in `variables.tf` and can be set via `terraform.tfvars` or environment variables: | Variable | Description | Type | Default | Required | |----------|-------------|------|---------|----------| | `container_name` | Name of the Renovate container | `string` | `"renovate"` | No | | `renovate_image` | Docker image for Renovate | `string` | `"renovate/renovate:latest"` | No | | `restart_policy` | Restart policy for the container | `string` | `"unless-stopped"` | No | | `memory_limit` | Memory limit for the container in MB | `number` | `2048` | No | | `memory_swap_limit` | Memory swap limit in MB (-1 for unlimited) | `number` | `-1` | No | | `domain` | Domain name for the application | `string` | `"bsdserver.lan"` | No | | `dns_name` | DNS name for the Renovate service | `string` | `null` (uses container_name) | No | | `create_cname_record` | Whether to create a DNS CNAME record | `bool` | `false` | No | | `dns_servers` | List of DNS servers for hostname resolution | `list(string)` | `[]` (uses Docker default) | No | | `renovate_autodiscover` | Enable autodiscovery of repositories | `bool` | `true` | No | | `renovate_onboarding_config` | Onboarding configuration for Renovate (JSON string) | `string` | See variables.tf | No | | `github_com_token` | GitHub.com token for fetching changelogs | `string` | `""` | No | | `log_level` | Log level for Renovate (debug, info, warn, error) | `string` | `"debug"` | No | | `extra_env_vars` | Additional environment variables for the container | `list(string)` | `[]` | No | | `upload_config_file` | Upload a config.js file to the container | `bool` | `true` | No | | `role_id` | Vault AppRole Role ID for authentication | `string` | - | Yes | | `secret_id` | Vault AppRole Secret ID for authentication | `string` | - | Yes | | `vault_skip_tls_verify` | Skip TLS verification for Vault (self-signed certs) | `bool` | `true` | No | ### Vault-Stored Configuration These values are **NOT** Terraform variables. They must be stored in HashiCorp Vault at path `secret/renovate`: | Vault Key | Description | Required | |-----------|-------------|----------| | `renovate_platform` | Git platform (must be "gitea") | Yes | | `renovate_endpoint` | Gitea API endpoint (e.g., `https://git.example.com/api/v1/`) | Yes | | `renovate_token` | Gitea Personal Access Token for the bot user | Yes | | `renovate_git_author` | Git commit author (e.g., `"Renovate Bot "`) | Yes | | `renovate_username` | Gitea username of the bot account (e.g., `"renovate-bot"`) | Yes | **See the "Store Credentials in Vault" section** in Gitea Bot Setup for complete instructions on storing these values. ## Outputs | Output | Description | |--------|-------------| | `container_id` | ID of the Renovate container | | `container_name` | Name of the Renovate container | | `config_volume` | Name of the config volume | | `cache_volume` | Name of the cache volume | | `renovate_platform` | Platform configured for Renovate | | `renovate_endpoint` | API endpoint configured for Renovate | ## Configuration Details ### Hardcoded Values The following values are hardcoded in `provider.tf` and may need customization: - **Docker Host**: `tcp://192.168.2.170:2376` (provider.tf:26) - **Vault Address**: `https://wbyc-srv-docker01.bsdserver.lan:8200` (provider.tf:33) **Note**: Backend configuration (MinIO/S3) is no longer hardcoded. Configure it via: - Environment variables: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` - Command-line flags during `terraform init` (see "Configure Backend" section) - Backend configuration file (`backend.hcl`) ### Security Considerations ✅ **Security Improvements**: 1. **No Privileged Mode**: Container runs without elevated privileges 2. **No Root User**: Runs as standard user 3. **No Docker Socket**: Docker socket is not mounted 4. **Resource Limits**: Memory limits prevent resource exhaustion 5. **Token Security**: Renovate token is marked as sensitive in Terraform 6. **Vault Integration**: DNS credentials stored securely in Vault ⚠️ **Security Notes**: 1. **Backend Credentials**: Use environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) instead of hardcoding 2. **Token Management**: Store `renovate_token` in Vault or use environment variables 3. **Repository Access**: Ensure bot user only has access to intended repositories 4. **Log Retention**: Logs are sent to Docker daemon - ensure proper retention policies 5. **Network Security**: Renovate connects to external APIs - ensure proper firewall rules ## Providers This module uses the following Terraform providers: - **docker** (kreuzwerker/docker v3.0.2): For Docker resource management - **vault** (hashicorp/vault v3.25.0): For secrets management - **dns**: For DNS record management with TSIG authentication ## Managing Configuration ### Option 1: Use Template File (Recommended) The module includes a `config.js.tpl` template that automatically configures Renovate based on your variables. Set `upload_config_file = true` to use this method. ### Option 2: Manual Configuration Mount a custom config.js file to the config volume: ```bash # Create config.js cat > config.js << 'EOF' module.exports = { platform: 'gitea', endpoint: 'https://gitea.example.com/api/v1/', gitAuthor: 'Renovate Bot ', username: 'renovate-bot', autodiscover: true, onboardingConfig: { $schema: 'https://docs.renovatebot.com/renovate-schema.json', extends: ['config:recommended'], }, }; EOF # Copy to volume docker run --rm -v renovate-config:/config -v $(pwd):/source alpine \ cp /source/config.js /config/ # Restart container docker restart renovate ``` ### Option 3: Environment Variables Only Set `upload_config_file = false` and rely solely on environment variables configured in the module. ## Scheduling Renovate Runs Renovate can be scheduled using various methods: ### Option 1: Cron Job ```bash # Add to crontab to run daily at 2 AM 0 2 * * * docker restart renovate ``` ### Option 2: Gitea Actions/Workflows Create `.gitea/workflows/renovate.yaml` in a dedicated repository: ```yaml name: Renovate on: schedule: - cron: '0 2 * * *' workflow_dispatch: jobs: renovate: runs-on: ubuntu-latest steps: - name: Trigger Renovate run: | docker restart renovate || true ``` ### Option 3: System Timer Create a systemd timer for automated scheduling. ## Troubleshooting ### Container Won't Start ```bash docker logs renovate ``` Check for: - Invalid Gitea endpoint or token - Network connectivity issues - Missing configuration - Resource limit issues ### Renovate Not Creating PRs Verify: 1. Bot user has write access to repositories 2. Gitea token has correct permissions 3. Repositories have valid `renovate.json` configuration 4. Check logs for API errors: `docker logs renovate` ### Authentication Failures - Verify token scopes in Gitea settings - Ensure token hasn't expired - Check endpoint URL is correct (should end with `/api/v1/`) - Verify bot user account is active ### DNS Record Not Created - Verify Vault DNS credentials are correct - Check DNS server allows dynamic updates - Ensure TSIG key has proper permissions ### Rate Limiting Issues If you see rate limit errors: 1. Add `github_com_token` for GitHub.com changelog access 2. Configure `prConcurrentLimit` in repository config 3. Adjust scheduling to reduce API calls ## Maintenance ### Updating Renovate ```bash # Pull latest image docker pull renovate/renovate:latest # Recreate container terraform apply -replace=docker_container.renovate ``` ### Viewing Logs ```bash # Container logs docker logs renovate -f # Filter for specific repository docker logs renovate 2>&1 | grep "repository-name" ``` ### Backup The Renovate data is stored in Docker volumes. To backup: ```bash # Backup config docker run --rm -v renovate-config:/data -v $(pwd):/backup alpine \ tar czf /backup/renovate-config-backup.tar.gz /data # Backup cache docker run --rm -v renovate-cache:/data -v $(pwd):/backup alpine \ tar czf /backup/renovate-cache-backup.tar.gz /data ``` ### Restore ```bash # Restore config docker run --rm -v renovate-config:/data -v $(pwd):/backup alpine \ tar xzf /backup/renovate-config-backup.tar.gz -C / # Restore cache docker run --rm -v renovate-cache:/data -v $(pwd):/backup alpine \ tar xzf /backup/renovate-cache-backup.tar.gz -C / ``` ## Advanced Configuration ### Custom Renovate Image To use a specific version: ```hcl module "renovate" { source = "./terraform-docker-renovate" renovate_image = "renovate/renovate:37.100.0" # ... other variables } ``` ### Multiple Platform Support While this module is optimized for Gitea, you can configure it for other platforms: ```hcl module "renovate_github" { source = "./terraform-docker-renovate" renovate_platform = "github" renovate_endpoint = "https://api.github.com/" renovate_token = var.github_token # ... other variables } ``` ### SonarQube Integration Add SonarQube scanning to Renovate PRs by configuring your repository's `renovate.json`: ```json { "extends": ["config:recommended"], "postUpgradeTasks": { "commands": [ "sonar-scanner -Dsonar.projectKey=myproject" ] } } ``` ## Integration with CI/CD ### Gitea Actions Integration Renovate PRs can automatically trigger Gitea Actions workflows. Example `.gitea/workflows/test.yaml`: ```yaml name: Test Dependencies on: pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run Tests run: | docker-compose up -d docker-compose exec -T app npm test ``` ### Pipeline Configuration For automated testing and deployment: 1. Configure branch protection rules in Gitea 2. Require status checks to pass before merging 3. Enable auto-merge in `renovate.json` for passing PRs ## Best Practices 1. **Start with Manual Approval**: Don't enable automerge until you trust the process 2. **Use Dependency Dashboard**: Enable `"dependencyDashboard": true` for visibility 3. **Schedule Updates**: Use `schedule` to avoid overwhelming your team 4. **Group Updates**: Group related dependencies to reduce PR noise 5. **Test Updates**: Always have CI/CD tests run on Renovate PRs 6. **Monitor Logs**: Regularly check Renovate logs for errors 7. **Pin Versions**: Use semantic versioning tags instead of `latest` ## Example Workflow 1. **Initial Setup**: - Create Renovate bot user in Gitea - Generate access token with required scopes - Deploy this Terraform module - Add bot as collaborator to repositories 2. **Repository Configuration**: - Add `renovate.json` to repository root - Configure package rules and schedules - Enable dependency dashboard 3. **First Run**: - Renovate creates onboarding PR - Review and merge onboarding PR - Renovate starts scanning dependencies 4. **Ongoing**: - Renovate creates PRs for updates - CI/CD tests run automatically - Review and merge PRs - Monitor dependency dashboard ## Resources - [Renovate Documentation](https://docs.renovatebot.com/) - [Gitea Platform Configuration](https://docs.renovatebot.com/modules/platform/gitea/) - [Self-Hosting Renovate](https://docs.renovatebot.com/examples/self-hosting/) - [Configuration Options](https://docs.renovatebot.com/configuration-options/) - [Renovate Docker Hub](https://hub.docker.com/r/renovate/renovate) ## License This module is part of the webuildyourcloud automation infrastructure. ## Contributing When contributing, ensure: - Terraform code follows best practices - Variables are properly documented - Security implications are considered - State backend configuration is tested - Configuration examples are validated