This project demonstrates Dagger services with container-to-container networking and local exposure.
Dagger is a programmable CI/CD engine that runs pipelines as containers. Instead of YAML configuration, you write type-safe Go code that works identically across local development and CI environments.
Key benefits:
- Same build locally and in CI (no "works on my machine")
- Fast feedback with aggressive caching
- Portable across any CI platform
- Programmatic service orchestration
Container networking via Dagger service bindings:
webapp.WithServiceBinding("redis", redisService)
// Creates DNS entry: webapp can now connect to "redis:6379"Complete isolated stacks for testing:
- Redis + webapp spun up per test
- No port conflicts between parallel runs
- Automatic cleanup
Deterministic container builds with layer caching:
- First build: ~30s
- Cached builds: ~2s
- Version-pinned dependencies
GitHub Actions runs the same Dagger functions as local development.
Local Machine
│
├─── Dagger CLI
│ │
│ └─── Dagger Engine (BuildKit)
│ │
│ ├─── Webapp Container (Go)
│ │ ↓
│ └─── Redis Container
│ (Service Binding: "redis")
│
└─── Port Forward: localhost:8080 → webapp:8080
Tech Stack:
- Dagger v0.19.2
- Go 1.21
- Redis 7
- GitHub Actions
# Install Dagger
curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.19.2 sh
sudo mv bin/dagger /usr/local/bin
# Clone and test
git clone https://github.com/ram-git-dev/dagger-services-example.git
cd dagger-services-example
# Run integration test
dagger call simple-integration-test
# Start local environment
dagger call expose-locally up --ports 8080:8080curl http://localhost:8080/health # Health check
curl http://localhost:8080/counter # Increment counter (Redis)
curl -X POST http://localhost:8080/set/key/value
curl http://localhost:8080/get/key├── .github/workflows/ # CI/CD pipeline
├── app/ # Go web application
│ ├── main.go # HTTP server + Redis integration
│ └── go.mod # App dependencies
├── main.go # Dagger functions (pipeline code)
├── dagger.json # Dagger module config
└── go.mod # Dagger SDK dependencies
Key distinction:
main.go(root) = Infrastructure/CI codeapp/main.go= Application code
# Build container
dagger call build-web-app
# Run integration tests
dagger call simple-integration-test
# Local development
dagger call expose-locally up --ports 8080:8080See COMMANDS.md for full reference.
Runs automatically on every push:
├─ Install Dagger v0.19.2
├─ Build web app
└─ Run integration testsPipeline executes the same Dagger functions used locally. No separate CI configuration needed.
View runs: Actions tab
redisService := dag.Container().From("redis:7-alpine").AsService()
webapp.WithServiceBinding("redis", redisService)Creates hostname redis that resolves to the Redis container. Application connects to redis:6379 - no hardcoded IPs.
Layer-based caching speeds up rebuilds:
- Changed code → Rebuild from that layer onwards
- Unchanged code → Use cached layers
- Dependencies cached separately from application code
Same inputs always produce same outputs. Critical for:
- Reproducible builds
- Debugging CI issues locally
- Consistent deployments
| Operation | Cold | Cached |
|---|---|---|
| Build | 15s | 2s |
| Integration test | 3s | 3s |
| CI pipeline | 45s | 30s |
MIT - See LICENSE for details.