Cloud Run’s sidecar containers can intercept and modify network traffic and log output, acting as a transparent proxy or logging agent without requiring application code changes.

Let’s see it in action. Imagine you have a simple Python web app running in Cloud Run, and you want to collect all its outgoing HTTP requests for analysis. You don’t want to modify the Python app itself.

# app.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

This app just serves a "Hello, World!" message on port 8080. Now, let’s add a sidecar. We’ll use curl in a separate container to make an external request from within the same Cloud Run service.

Here’s a Dockerfile for our main application:

# Dockerfile for main application
FROM python:3.9-slim
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]

And here’s a Dockerfile for our sidecar, which will use curl to make a request and tcpdump to capture traffic:

# Dockerfile for sidecar
FROM ubuntu:latest
RUN apt-get update && apt-get install -y curl tcpdump
CMD ["sh", "-c", "echo 'Starting sidecar...' && tcpdump -i any -w /var/log/capture.pcap & curl http://example.com && sleep infinity"]

To deploy this as a single Cloud Run service with two containers, you’d use the gcloud CLI:

gcloud run deploy my-sidecar-app \
  --image gcr.io/YOUR_PROJECT_ID/my-app-image \
  --add-cloudsql-instances YOUR_CLOUDSQL_INSTANCE \
  --port 8080 \
  --cpu 1 \
  --memory 512Mi \
  --set-env-vars "PORT=8080" \
  --platform managed \
  --region us-central1 \
  --execution-environment gen2 \
  --service-account YOUR_SERVICE_ACCOUNT@YOUR_PROJECT_ID.iam.gserviceaccount.com \
  --no-traffic \
  --command "gcloud run services replace -" <<EOF
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: my-sidecar-app
spec:
  template:
    spec:
      containers:
      - image: gcr.io/YOUR_PROJECT_ID/my-app-image # Replace with your main app image
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: "1"
            memory: "512Mi"
      - image: gcr.io/YOUR_PROJECT_ID/my-sidecar-image # Replace with your sidecar image
        resources:
          limits:
            cpu: "1"
            memory: "512Mi"
        command: ["sh", "-c", "echo 'Starting sidecar...' && tcpdump -i any -w /var/log/capture.pcap & sleep infinity"] # Simplified for example
EOF

This configuration tells Cloud Run to launch both containers within the same execution environment. The app.py container listens on port 8080, and the sidecar container starts tcpdump to capture traffic and then runs curl to hit an external site. The sleep infinity keeps the sidecar container alive.

The core problem this solves is adding cross-cutting concerns – like logging, security, or request routing – to applications without modifying their original code. It’s like putting a universal adapter on your existing electronics. The sidecar container runs alongside your main application container, sharing the same network namespace and often the same filesystem. This means the sidecar can "see" and interact with the main application’s network traffic and files as if they were part of the same process.

Internally, Cloud Run uses Knative’s Service object, which allows for multiple containers per pod (or execution environment in Cloud Run’s case). When you define multiple containers in the spec.template.spec.containers array, Knative ensures they are co-located. They share a network stack, meaning localhost refers to the same network interface for all containers in that execution environment. This is crucial for proxying; a sidecar can bind to localhost:80 and forward traffic to the main app on localhost:8080.

The exact levers you control are the image, command, args, ports, volumeMounts, and env for each container. For a proxy, you’d typically have the sidecar listen on the port specified by Cloud Run (often 8080 or whatever PORT environment variable is set to) and forward requests to your application’s container, which might be listening on a different internal port. For logging, the sidecar can tail application logs, collect metrics, or, as shown, capture network traffic.

One key aspect often overlooked is how shared volumes work with sidecars. If your main application writes logs to /var/log/app.log, and your sidecar mounts a volume at /var/log and then tailors that file, the sidecar has direct access to the application’s output. This direct access, facilitated by shared volumes and namespaces, is what makes sidecars so powerful for observability and management tasks without application instrumentation.

The next hurdle you’ll likely encounter is managing the lifecycle of these containers, especially ensuring the sidecar doesn’t exit prematurely if it’s meant to run continuously.

Want structured learning?

Take the full Cloud-run course →