This article explains how to implement a blue-green deployment strategy for a Go application using Docker Compose and Caddy on a Raspberry Pi. Blue-green deployment is a technique that reduces downtime by running two identical environments called Blue and Green.
Prerequisites
Before following this tutorial, make sure you have read the blog post Deploying Applications to Raspberry Pi with Docker Compose (from my Mac). This post explains how to set up a Docker remote context to easily deploy applications to a remote machine like a Raspberry Pi.
Project Structure
Our project consists of the following files:
main.go
: A simple Go web serverDockerfile
: Instructions to build the Go applicationcompose.yaml
: Docker Compose configuration for our servicesCaddyfile
: Caddy reverse proxy configurationcaddy.Dockerfile
: Dockerfile for Caddy setup
Understanding the Components
The Go Application
Our application is a simple web server that displays a coloured greeting. The colour (blue or green) helps us identify which version is currently running:
package main
import (
"log"
"net/http"
"os"
)
func main() {
var httpPort = os.Getenv("HTTP_PORT")
mux := http.NewServeMux()
mux.HandleFunc("/", func(response http.ResponseWriter, request *http.Request) {
response.Header().Add("Content-Type", "text/html;charset=utf-8")
response.Write([]byte("<h1>🔵 👋 Hello World 🌍</h1>")) // Blue version
})
log.Println("🌍 http server is listening on: " + httpPort)
errListening := http.ListenAndServe(":"+httpPort, mux)
log.Fatal(errListening)
}
Docker Compose Configuration
The compose.yaml
file defines three services:
web-app-blue
: Blue version running on port 6065web-app-green
: Green version running on port 7065caddy
: Reverse proxy running on port 9090
services:
web-app-blue:
build:
context: .
dockerfile: Dockerfile
environment:
- HTTP_PORT=6065
ports:
- 6065:6065
networks:
- proxy-network
web-app-green:
build:
context: .
dockerfile: Dockerfile
environment:
- HTTP_PORT=7065
ports:
- 7065:7065
networks:
- proxy-network
caddy:
build:
context: .
dockerfile: caddy.Dockerfile
ports:
- "9090:9090"
networks:
- proxy-network
networks:
proxy-network:
driver: bridge
Implementation Steps
1. Deploy the Blue Version
First, deploy (and build) the blue version of the application:
docker compose up -d --build web-app-blue
You can verify the deployment by accessing http://robby.local:6065
in your browser.
2. Set Up the Reverse Proxy
Configure Caddy to route traffic to the blue version:
:9090 {
reverse_proxy web-app-blue:6065
log {
output stdout
format console
}
}
Deploy Caddy:
docker compose up -d --build caddy
Now you can access the application through the reverse proxy at http://robby.local:9090
.
3. Deploy the Green Version
Update the application code to display the green version.
Change the response message in main.go
:
response.Write([]byte("<h1>🔵 👋 Hello World 🌍</h1>")) // Blue version
by:
response.Write([]byte("<h1>🟢 👋 Hello World 🌍</h1>")) // Green version
Then deploy (and build) the green version:
docker compose up -d --build web-app-green
Verify the green version at http://robby.local:7065
.
4. Switch Traffic to Green Version
Update the Caddyfile to route traffic to the green version:
:9090 {
reverse_proxy web-app-green:7065
log {
output stdout
format console
}
}
Redeploy Caddy to switch the traffic:
docker compose up -d --build caddy
Now you can access the green version of the application through the reverse proxy at http://robby.local:9090
.
Testing the Deployment
After each step, you can verify the deployment:
Blue version:
http://robby.local:6065
Green version:
http://robby.local:7065
Active version (through Caddy):
http://robby.local:9090
Benefits of This Approach
Zero-downtime deployments: Users always have access to one version of the application
Easy rollback: If issues are found in the green version, you can quickly switch back to blue
Verification: You can test the new version before routing traffic to it
Simple implementation: Uses standard Docker Compose and Caddy features
Conclusion
This setup provides a simple but effective way to implement blue-green deployments on a Raspberry Pi. While this example shows manual switching between versions, you could automate this process using scripts or CI/CD pipelines for production environments.