Multi-Service Docker Compose Build in Travis CI with Cross-Container Caching and Service Health Checks

I’m working on a Travis CI build that spins up a complex multi-service environment using Docker Compose. The goal is to run integration tests that require multiple containers to be networked together — including a custom app container, a PostgreSQL service, Redis, and a local S3-compatible mock (like MinIO).

My setup uses Docker Compose to define all of these containers, and I rely on Travis CI’s docker service to run them within the build. I’m launching the services during the before_install phase with docker-compose up -d, and I’ve added healthcheck definitions to each service so that Travis CI doesn’t begin testing until the services are fully healthy. However, I’m noticing that Travis sometimes proceeds before services like PostgreSQL or MinIO are truly ready, resulting in sporadic test failures.

In addition to reliability issues, I’m also struggling with Docker layer caching. Despite attempting to cache $HOME/.docker and using docker save / docker load to persist custom images, my builds still take over 10 minutes because images appear to be rebuilt every time. I’ve experimented with Travis’ native caching and also tried tagging and reloading images within the CI job itself, but it doesn’t seem to help much.

There’s also a networking challenge. Occasionally, containers cannot resolve each other by name. For example, the app container sometimes fails to connect to minio:9000, even though all services are defined in the same docker-compose.yml and should be on the same network. These issues appear intermittently, which makes them especially difficult to diagnose.

I’d love to hear from anyone who has successfully built a Docker Compose-based integration environment on Travis CI. Any tips on caching strategies, service readiness, or job structure improvements would be greatly appreciated. Let me know if you’d like me to share a link to the public GitHub repository for more context.

While Docker Compose supports healthcheck, Travis won’t inherently wait for these checks to pass unless you tell it to. You can create a scripts/wait-for-health.sh script that loops over your critical services (such as postgres, redis, and minio) and checks their health status using docker inspect. This ensures your build doesn’t proceed until all containers are reported as healthy.

Here’s what that script should do: for each service, it checks if docker inspect -f '{{.State.Health.Status}}' returns healthy, and if not, it waits and tries again every few seconds. Once all services pass, the script completes and the test phase can safely begin. Don’t forget to mark this script as executable with chmod +x scripts/wait-for-health.sh, and then invoke it during your before_install phase in .travis.yml right after docker-compose up -d.

For Docker layer caching, the common mistake is assuming that simply caching $HOME/.docker will persist image layers — but this rarely works reliably across Travis CI builds. Instead, you should explicitly save your Docker image as a tarball using docker save before the cache phase, and then load it again in a future build using docker load. For example, you could use docker save my-app:latest > my-app.tar in the before_cache section, and restore it in before_install with docker load < my-app.tar. This approach makes Docker image reuse deterministic and avoids layer rebuilds on every build.

In summary, you’ll need to create a bash script to wait for health checks, explicitly manage Docker caching with docker save/load, and stick to container names for intra-service communication. These fixes combined will make your Travis + Docker Compose setup faster, more stable, and easier to debug. Let me know if you want a sample repo to demonstrate it all.