Cloud Build·

How to set environment variables using Google Cloud Build

This may seem like a simple task, but... it's not.

Let's talk about something that might seem straightforward but can get quite tricky :

handling environment variables when building Docker images with Cloud Build.

I'll share some insights from my experience and break down how to do it properly.

The Basic Setup

First, let's look at what we're trying to achieve. We want to:

  • Build a Docker image using Cloud Build
  • Pass environment variables securely
  • Make sure these variables are available during build time and runtime

Understanding the Moving Parts

We're dealing with three main components:

  • Cloud Build configuration (cloudbuild.yaml)
  • Dockerfile
  • Environment variables

The Cloud Build Configuration

Let's break down our Cloud Build configuration:

steps:
  - name: 'alpine'
    entrypoint: 'sh'
    args:
      - '-c'
      - |
        echo $_DB_HOST
        # ... other echo statements

This first step is actually a clever debug step! It helps us verify that our substitution variables are being passed correctly.

Pretty handy when things go wrong 🤓

The Tricky Part: Variable Passing

Here's where it gets interesting (and potentially confusing).

We have three levels of variable passing:

  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'gcr.io/$PROJECT_ID/my-image', '.']
    env:
      - 'DB_HOST=$_DB_HOST'
      - 'DB_NAME=$_DB_NAME'
      # ... other variables

What are the substitutions variables?

Substitution variables are defined in the Cloud Build configuration file and are replaced with their values during the build process.

substitutions:
  _DB_HOST: your-host
  _DB_NAME: your-db
  _DB_PASSWORD: your-password
  _DB_PORT: your-port
  _DB_USER: your-user

You can define these variables in the cloudbuild.yaml file or in the Cloud Build UI.

Cloud Build Substitution Variables

Common Gotchas and Limitations 😅

Substitution Variable Naming: Cloud Build substitution variables MUST start with '_' (underscore). This is a common source of confusion!

Variable Scope: Variables defined in Cloud Build don't automatically transfer to your Docker build. You need to explicitly pass them through.

Build vs Runtime: Remember that ARG variables in Dockerfile are only available during build time. That's why we need to convert them to ENV variables if we want them at runtime.

Best Practices

Debug First

Always include a debug step (like our alpine step) to verify variables are being passed correctly.

Use ARG and ENV Together

In your Dockerfile, use this pattern:

ARG DB_HOST
ENV DB_HOST=$DB_HOST

Security Considerations

Remember that environment variables in your final image can be viewed by anyone with access to the image. Consider using secrets management for sensitive data.

Example: Node.js App

Here's an example of how you can set up environment variables for a Node.js app:

FROM node:18-alpine

WORKDIR /app

# Pass build-time variables
ARG DB_HOST
ARG DB_NAME
ARG DB_PASSWORD
ARG DB_PORT
ARG DB_USER

ENV DB_HOST=$DB_HOST
ENV DB_NAME=$DB_NAME
ENV DB_PASSWORD=$DB_PASSWORD
ENV DB_PORT=$DB_PORT
ENV DB_USER=$DB_USER

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run build

EXPOSE 3000

CMD ["npm", "start"]

Then we need to have a .env file with our environment variables:

DB_HOST=your-host
DB_NAME=your-db
DB_PASSWORD=your-password
DB_PORT=your-port
DB_USER=your-user

for cloud build, we need to have a cloudbuild.yaml file:

steps:
  # Debug step to verify variables are being passed correctly
  - name: 'alpine'
    entrypoint: 'sh'
    args:
      - '-c'
      - |
        echo $_DB_HOST
        echo $_DB_NAME
        echo $_DB_PASSWORD
        echo $_DB_PORT
        echo $_DB_USER

  # Build the Docker image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'gcr.io/$PROJECT_ID/my-image', '.']
    env:
      - 'DB_HOST=$_DB_HOST'
      - 'DB_NAME=$_DB_NAME'
      - 'DB_PASSWORD=$_DB_PASSWORD'
      - 'DB_PORT=$_DB_PORT'
      - 'DB_USER=$_DB_USER'

  # Push the Docker image to Google Container Registry
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'gcr.io/$PROJECT_ID/my-image']

images:
  - 'gcr.io/$PROJECT_ID/my-image'

options:
  logging: 'CLOUD_LOGGING_ONLY'  # Log build output to Cloud Logging only

Building locally w/o gcloud

To build your setup locally, you can use the docker CLI:

docker build \
  --build-arg DB_HOST=$(grep DB_HOST .env | cut -d '=' -f2) \
  --build-arg DB_NAME=$(grep DB_NAME .env | cut -d '=' -f2) \
  --build-arg DB_PASSWORD=$(grep DB_PASSWORD .env | cut -d '=' -f2) \
  --build-arg DB_PORT=$(grep DB_PORT .env | cut -d '=' -f2) \
  --build-arg DB_USER=$(grep DB_USER .env | cut -d '=' -f2) \
  -t node-app .

This command reads the environment variables from a .env file and passes them to the Docker build process.

Conclusion

While setting up environment variables in Cloud Build might seem complicated at first, it follows a logical pattern once you understand the flow.


Copyright © 2025. All rights reserved.