Skip to main content
Sidecars are containers that run alongside the Steps in your Task, providing supporting services like databases, Docker daemons, or SSH servers. This guide shows you how to use sidecars effectively.

Overview

Sidecars enable you to:
  • Run services that Steps need to communicate with
  • Provide test dependencies like databases or message queues
  • Run Docker-in-Docker for building container images
  • Set up SSH servers or other network services
  • Share resources with Steps through volumes
Sidecars start before Steps execute and run throughout the Task execution.

Basic Sidecar Configuration

Define sidecars in the Task specification:
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  generateName: sidecar-example-
spec:
  taskSpec:
    steps:
      - name: main
        image: alpine
        script: |
          echo "Main step running"
          # Steps can communicate with sidecar
    sidecars:
      - name: helper
        image: nginx

Docker-in-Docker Sidecar

A common use case is running Docker commands from Steps:
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  generateName: dind-sidecar-
spec:
  taskSpec:
    steps:
      - name: client
        image: docker
        env:
          # Connect to the sidecar over TCP, with TLS
          - name: DOCKER_HOST
            value: tcp://localhost:2376
          # Verify TLS
          - name: DOCKER_TLS_VERIFY
            value: '1'
          # Use the certs generated by the sidecar daemon
          - name: DOCKER_CERT_PATH
            value: /certs/client
        workingDir: /workspace
        script: |
          #!/usr/bin/env sh
          set -e
          # Run a Docker container
          docker run busybox echo hello

          # Write a Dockerfile and build it
          cat > Dockerfile << EOF
          FROM ubuntu
          RUN apt-get update
          ENTRYPOINT ["echo", "hello"]
          EOF
          docker build -t hello .
          docker images

          # Run the built image
          docker run hello
        volumeMounts:
          - mountPath: /certs/client
            name: dind-certs

    sidecars:
      - name: server
        image: docker:dind
        args:
          - --storage-driver=vfs
          - --userland-proxy=false
          - --debug
        computeResources:
          requests:
            memory: "512Mi"
        securityContext:
          privileged: true
        env:
          # Write generated certs to the path shared with the client
          - name: DOCKER_TLS_CERTDIR
            value: /certs
        volumeMounts:
          - mountPath: /certs/client
            name: dind-certs
        # Wait for the dind daemon to generate the certs
        startupProbe:
          periodSeconds: 1
          failureThreshold: 30
          exec:
            command: ['ls', '/certs/client/ca.pem']
        # Verify Docker daemon is ready to accept commands
        readinessProbe:
          periodSeconds: 2
          exec:
            command: ['docker', 'info']

    volumes:
      - name: dind-certs
        emptyDir: {}
Docker-in-Docker requires privileged mode. Ensure your cluster security policies allow privileged containers.

Sharing Data with Volumes

Sidecars and Steps can share data through volumes:
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  generateName: shared-volume-
spec:
  taskSpec:
    volumes:
      - name: messages
        emptyDir: {}
    steps:
      - name: write
        image: alpine
        volumeMounts:
          - name: messages
            mountPath: /messages
        script: |
          echo "Hello from step" > /messages/greeting.txt
      - name: read
        image: alpine
        volumeMounts:
          - name: messages
            mountPath: /messages
        script: |
          cat /messages/greeting.txt
    sidecars:
      - name: processor
        image: alpine
        volumeMounts:
          - name: messages
            mountPath: /messages
        command: ['sh', '-c']
        args:
          - |
            while true; do
              if [ -f /messages/greeting.txt ]; then
                echo "Sidecar saw: $(cat /messages/greeting.txt)"
              fi
              sleep 2
            done

Sidecar Readiness

Use readiness probes to ensure sidecars are ready before Steps start:
sidecars:
  - name: database
    image: postgres:13
    env:
      - name: POSTGRES_PASSWORD
        value: example
    readinessProbe:
      exec:
        command:
          - pg_isready
          - -U
          - postgres
      periodSeconds: 2
      initialDelaySeconds: 5
Steps wait for sidecars with readiness probes to become ready before executing.

Startup Probes

Use startup probes for sidecars that need extra time to initialize:
sidecars:
  - name: slow-service
    image: my-slow-service
    startupProbe:
      periodSeconds: 1
      failureThreshold: 30  # Allow up to 30 seconds for startup
      exec:
        command: ['cat', '/tmp/ready']
    readinessProbe:
      periodSeconds: 2
      exec:
        command: ['curl', 'localhost:8080/health']

SSH Server Sidecar

Run an SSH server for Git operations:
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  name: authenticating-git-commands
spec:
  serviceAccountName: ssh-key-service-account
  taskSpec:
    volumes:
      - name: messages
        emptyDir: {}
    sidecars:
      - name: server
        image: alpine/git:v2.26.2
        securityContext:
          runAsUser: 0
        volumeMounts:
          - name: messages
            mountPath: /messages
        script: |
          #!/usr/bin/env ash

          # Generate a private host key
          ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
          chmod 0600 /etc/ssh/ssh_host_rsa_key*
          HOST_PUBLIC_KEY=$(cat /etc/ssh/ssh_host_rsa_key.pub | awk '{ print $2 }')
          echo "localhost ssh-rsa $HOST_PUBLIC_KEY" > /messages/known_hosts

          # Wait for Steps to supply the server a public key
          while [ ! -f /messages/authorized_keys ] ; do
            sleep 1
          done

          # Configure SSH to allow login
          mkdir /root/.ssh
          cp /messages/authorized_keys /root/.ssh/
          sed -i s/root:!/'root:*'/g /etc/shadow

          # Create a git repo
          cd /root/
          mkdir repo
          cd repo
          git init . --bare

          # Start the sshd server
          /usr/sbin/sshd -E /var/log/sshd
          touch /messages/sshd-ready
          tail -f /var/log/sshd
    steps:
      - name: setup
        image: alpine/git:v2.26.2
        volumeMounts:
          - name: messages
            mountPath: /messages
        script: |
          # Generate authorized_keys from credentials
          ssh-keygen -y -f $(credentials.path)/.ssh/id_* > /messages/authorized_keys

          # Wait for sshd to start
          while [ ! -f /messages/sshd-ready ] ; do
            sleep 1
          done
      - name: git-clone-and-push
        image: alpine/git:v2.26.2
        volumeMounts:
          - name: messages
            mountPath: /messages
        script: |
          ln -s $(credentials.path)/.ssh /root/.ssh
          cp /messages/known_hosts /root/.ssh/
          git clone root@localhost:/root/repo ./repo
          cd repo
          echo "Hello, world!" > README
          git add README
          git commit -m "Test commit"
          git push origin master

Workspaces in Sidecars

Sidecars can access Task workspaces:
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: sidecar-workspace-example
spec:
  workspaces:
    - name: source
  sidecars:
    - name: file-server
      image: python:3-alpine
      workingDir: $(workspaces.source.path)
      command: ['python', '-m', 'http.server', '8000']
  steps:
    - name: download
      image: alpine
      script: |
        echo "File server available at localhost:8000"
        # Step can access files via HTTP from sidecar

Variable Substitution in Sidecars

Sidecars support variable substitution:
sidecars:
  - name: database
    image: $(params.database-image)
    env:
      - name: DB_NAME
        value: $(params.database-name)
      - name: WORKSPACE_PATH
        value: $(workspaces.data.path)
Supported fields:
  • spec.sidecars[].name
  • spec.sidecars[].image
  • spec.sidecars[].imagePullPolicy
  • spec.sidecars[].env.value
  • spec.sidecars[].command
  • spec.sidecars[].args
  • spec.sidecars[].script
  • spec.sidecars[].volumeMounts

Sidecar Lifecycle

Startup Order

  1. Sidecars start first
  2. Sidecars with readiness probes must become ready
  3. Steps execute in order
  4. Sidecars continue running during Step execution

Shutdown

Sidecars automatically stop when:
  • All Steps complete successfully
  • A Step fails and the Task stops
  • The TaskRun times out
Tekton sends a SIGTERM to sidecars to request graceful shutdown. If sidecars don’t exit within the grace period, they receive SIGKILL.

Resource Management

Specify resource requests and limits for sidecars:
sidecars:
  - name: memory-intensive
    image: my-service
    computeResources:
      requests:
        memory: "512Mi"
        cpu: "500m"
      limits:
        memory: "1Gi"
        cpu: "1000m"

Best Practices

Use Readiness Probes

Always configure readiness probes for sidecars that Steps depend on. This prevents race conditions where Steps try to connect before the sidecar is ready.
sidecars:
  - name: api-server
    image: my-api
    readinessProbe:
      httpGet:
        path: /health
        port: 8080
      periodSeconds: 2

Minimize Sidecar Size

# Good: Lightweight sidecar
sidecars:
  - name: cache
    image: redis:alpine

# Avoid: Unnecessarily large images
sidecars:
  - name: cache
    image: ubuntu-with-redis

Share Data via Volumes

Use volumes instead of network communication when possible:
volumes:
  - name: shared-data
    emptyDir: {}
steps:
  - name: producer
    volumeMounts:
      - name: shared-data
        mountPath: /data
sidecars:
  - name: consumer
    volumeMounts:
      - name: shared-data
        mountPath: /data

Set Security Contexts

sidecars:
  - name: secure-service
    image: my-service
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      allowPrivilegeEscalation: false
      capabilities:
        drop:
          - ALL

Common Patterns

Database for Testing

sidecars:
  - name: postgres
    image: postgres:13
    env:
      - name: POSTGRES_PASSWORD
        value: test
      - name: POSTGRES_DB
        value: testdb
    readinessProbe:
      exec:
        command: ['pg_isready', '-U', 'postgres']
      periodSeconds: 2

Service Mesh Proxy

sidecars:
  - name: envoy
    image: envoyproxy/envoy:v1.20.0
    args:
      - -c
      - /config/envoy.yaml
    volumeMounts:
      - name: envoy-config
        mountPath: /config

Message Queue

sidecars:
  - name: rabbitmq
    image: rabbitmq:3-alpine
    env:
      - name: RABBITMQ_DEFAULT_USER
        value: guest
      - name: RABBITMQ_DEFAULT_PASS
        value: guest
    readinessProbe:
      exec:
        command: ['rabbitmq-diagnostics', 'ping']
      periodSeconds: 5
Check these common issues:
  1. Image pull errors: Verify the image exists and is accessible
  2. Resource constraints: Ensure the cluster has sufficient resources
  3. Security policies: Check if security contexts or policies block the sidecar
  4. Volume mount conflicts: Verify volume mounts don’t conflict with Step mounts
View sidecar logs:
kubectl logs <pod-name> -c <sidecar-name>
Steps communicate with sidecars via:
  1. Localhost networking: Sidecars listen on localhost or 127.0.0.1
  2. Shared volumes: Exchange files through mounted volumes
  3. Environment variables: Pass configuration via env vars
Example:
steps:
  - name: client
    script: |
      # Connect to sidecar on localhost
      curl http://localhost:8080/api

Build docs developers (and LLMs) love