Root Cause: Shippable's Container Reuse Model
Understanding Shippable's Job Architecture
Shippable jobs are container-based, and reuse base images for speed. However, if these base images are stale or mutable (i.e., lack content hashes), the pipeline's behavior can vary across runs—even without source code changes.
Common Symptoms
- Pipeline passes locally but fails on Shippable's environment
- Flaky tests when build caches are re-used
- Stuck builds due to stale or invalid Docker layers
- Security scanning fails due to outdated dependencies
Architectural Analysis
Ephemeral vs Persistent Runners
Shippable supports both persistent workers (hosted internally) and ephemeral cloud-based runners. When persistent nodes aren't cleaned, dangling containers or volumes can leak into future builds, breaking idempotency.
Shared Pipeline Resources
Teams often share pipeline YAML templates and container registries across services. Without version-locking or image pinning, this sharing introduces downstream failures triggered by subtle changes to unrelated services.
Diagnostic Approaches
1. Enabling Build Debug Mode
Append shippable_post
scripts with verbose logs to capture the build's runtime context. This includes inspecting the filesystem, image layers, and network availability:
jobs: - name: ci_build_debug type: runSh steps: - IN: repo_name - TASK: script: - df -h - docker images - env
2. Audit Cached Layers
Use the Shippable cache viewer or inspect /tmp/cache
contents if using on-prem workers. Compare cache timestamps with known commits to identify staleness.
3. Use Immutable Image Digests
Instead of referencing tags like python:3.9
, use SHA digests to pin dependencies:
image: python@sha256:ab234f...890
Step-by-Step Fixes
1. Force Clean Builds
Periodically invalidate caches and rebuild from scratch to ensure consistency. Use the UI or Shippable CLI:
shippable reset --build-cache --all-pipelines
2. Pin Docker Layers Explicitly
Update your pipeline YAML to use specific digests instead of floating tags:
jobs: - name: myjob type: runSh image: my-registry/app@sha256:123abc...
3. Prune Worker Nodes
Automate the pruning of Docker containers, volumes, and images on persistent nodes every 24 hours:
docker system prune -af --volumes
4. Validate Environment Checksums
Create checksum baselines for your build environments. Store these in CI logs and compare them during post-merge builds to catch image drift:
md5sum /etc/os-release
Best Practices for Long-Term Stability
- Use ephemeral workers when possible to ensure clean state
- Pin all container images by digest, not tag
- Perform weekly validation of pipeline config vs registry versions
- Centralize caching policies to avoid drift between services
- Log container diffs and prune stale resources regularly
Conclusion
Shippable's CI/CD model provides flexibility, but it requires discipline in image management, cache control, and build environment hygiene. Inconsistent builds often stem from seemingly unrelated architectural decisions—like unpinned containers or shared caches. By incorporating deterministic pipeline practices, stronger version pinning, and periodic clean-ups, teams can restore confidence in Shippable-driven delivery pipelines and reduce pipeline entropy over time.
FAQs
1. Why do Shippable builds pass locally but fail remotely?
Usually due to differences in base images, cache contents, or environment variables that drift between local and cloud environments.
2. Is it safe to run Docker prune in CI environments?
Yes, if properly scoped. Use it in ephemeral or non-production runners and automate it with scheduled tasks to keep environments clean.
3. What's the best way to verify that a Docker image hasn't changed?
Use SHA256 digests and image manifest validation. Avoid using tags alone, especially 'latest', which can point to mutable content.
4. Can Shippable support immutable infrastructure patterns?
Partially. While it lacks native immutability enforcement, you can simulate it with digest-pinned images and strict pipeline gating logic.
5. How can I detect when caches are causing flakiness?
Introduce a parallel cache-free build job and compare outputs. If failures vanish in the clean build, stale caches are likely the issue.