A template repository from the balena solutions team demonstrating how to work with multiple Docker Compose files in balena projects.
This template provides a workaround for two key limitations in the balena CLI:
- No
--fileflag support: The balena CLI doesn't support specifying which compose file to use (unlike standard Docker Compose) - No compose profiles support: Docker Compose profiles aren't currently supported in balena deployments
The target.sh script solves these issues by allowing you to maintain multiple compose files and select which one to deploy at build/push time.
This approach is particularly useful when you need to:
- Deploy different service configurations for different customers/vendors
- Maintain separate development, staging, and production compose files
- Switch between different hardware configurations
- Test various service combinations without modifying your main compose file
The target.sh script:
- Accepts a target compose file via the
--fileflag - Temporarily copies it to
docker-compose.yml(the default file balena expects) - Executes the balena CLI command with your specified action and flags
- Automatically cleans up the temporary file when done
This approach is safe and reliable—even if the script is interrupted, the cleanup trap ensures temporary files are removed.
./target.sh [build|push|deploy] --file <target-file> [balena-flags...]Push to a fleet using vendor A's configuration:
./target.sh push --file compose.vendorA.yml my-fleetBuild locally with vendor B's configuration:
./target.sh build --file compose.vendorB.ymlDeploy with debug output:
./target.sh push --file compose.vendorC.yml my-fleet --debugDeploy to a specific device:
./target.sh deploy --file compose.vendorA.yml my-device-uuidAn example project structure for 3 different vendors might look as follows:
.
├── compose.vendorA.yml # Compose file for vendor A
├── compose.vendorB.yml # Compose file for vendor B
├── compose.vendorC.yml # Compose file for vendor C
├── target.sh # Helper script for multi-file deployment
├── service-authenticator/ # Authentication service
├── service-ui/ # User interface service
├── service-vendorAspecific/ # Vendor-specific service
└── service-webserver/ # Web server service
-
Copy target.sh into your repo
-
Make the script executable:
chmod +x target.sh
-
Create your compose files:
- Edit
compose.vendorA.yml,compose.vendorB.yml, etc. to define your service configurations - Each compose file can include different services, environment variables, or configurations
- Edit
-
Deploy to your balena fleet:
./target.sh push --file compose.vendorA.yml <your-fleet-name>
- Never commit
docker-compose.yml: This file is temporary and automatically managed bytarget.sh. Add it to your.gitignoreif not already present. - Balena CLI required: You must have the balena CLI installed and be logged in.
- File validation: The script validates that your target compose file exists before attempting deployment.
- Automatic cleanup: The script uses a trap to ensure temporary files are cleaned up even if interrupted.
- Version control: Keep all your compose variants in version control to track configuration changes
- Naming convention: Use descriptive names for your compose files (e.g.,
compose.production.yml,compose.customer-acme.yml) - Testing: Always test with
buildlocally before pushing to a fleet - Documentation: Document the differences between your compose files in comments or separate docs
- This is a workaround, not a native feature—if balena CLI adds
--fileor profile support in the future, consider migrating - Each deployment requires explicitly specifying the target file
- The temporary
docker-compose.ymlfile could cause confusion if the script is interrupted (though cleanup should handle this)
For issues related to:
- This template: Open an issue in this repository
- Balena CLI: Visit the balena CLI repository
- Balena platform: Check the balena forums