How to Access ENV Variables in Google Cloud Run using Next.js
Tags: cloud, docker, webdev, programming
Published: 2022-04-14 23:31:48

If you’re building a web application, Google Cloud Run is a great option for you to deploy microservices to the cloud. It’s built on top of Kubernetes and allows you to scale backend and monorepos with ease.

However, when our team at [Ownchain](https://ownchain.it/) was working to deploy our users’ based on a Docker image sitting in Google Cloud Build and Container Registry, our client-side code was unable to access our environment variables set in Cloud Run–they were showing up as undefined.

Here’s how we were able to deploy our Next.js monorepo application to Google Cloud using Docker and dynamic ENV variables:

I’m assuming you have already setup lerna and can run you’re application locally.

## Initial Setup

This is the key to exposing your ENV variables to the client once you deploy your application to Cloud Run.

We ended up using @beam-australia/react-env to fetch ENV variables from the server and expose them to the client. 

You can follow the NEXT.js example in their repo, or they also have instructions if you used Create React App.
Here’s an example of how it works:

`// package.json
{
  "name": "web",
  "version": "0.1.0",
  "dependencies": {
    "@beam-australia/react-env": "^3.1.1",
    "next": "^11.0.1",
    "next-compose-plugins": "^2.2.1",
    "next-with-less": "^1.0.0",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "react-env": "^0.0.0",
    "react-router-dom": "^5.2.0",
  },
  "scripts": {
    "start": "react-env --path ../../../.env -- next dev",
    "build": "next build",
    "export": "next export",
    "start:prod": "react-env --path ../../../.env -- next start",
    "test": "jest",
  },
}`

Using the react-env package we can set a custom path for our .env file within our monorepo application.

`// app.tsx
import React, { useEffect } from 'react';
import Head from 'next/head';
import env from "@beam-australia/react-env";

function App() {
 useEffect(() => {
   console.log("Firebase User: ",  env("FIREBASE_USER_ID"))
  }, [])

  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Ownchain</title>
        <script src="/__ENV.js" />
      </Head>
    </>
  );
}`

By adding the script t our <Head> component we can read in our ENV variables using env("FIREBASE_USER_ID") format.

## Some Devops Stuff

Once that’s all setup, you should have your Dockerfile in the root directory ready to go. Our’s looked something like this:

`# Stage 1: Compile and Build the app
# Node version
FROM node:14.17.3-alpine as build
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat git
# Set the working directory
WORKDIR /app
# Add the source code to app
COPY ./js /app
# Install all the dependencies
RUN yarn install
RUN yarn bootstrap
# Generate the build of the application
RUN yarn build
# Stage 2: Serve app with nginx server
# Production image, copy all the files and run next
FROM node:14.17.3-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# Copy the build output to replace the default nginx contents.
COPY --from=build /app/packages/web/next.config.js ./
COPY --from=build /app/packages/web/public ./public
COPY --from=build --chown=nextjs:nodejs /app/packages/web/.next ./.next
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/packages/web/package.json ./package.json
USER root
EXPOSE 3000
CMD ["yarn", "start:prod"]`

**Make sure to add your USER as root otherwise @beam-australia/react-env won’t have enough access to your environment variables.

## Sync Github with CloudBuild and set Cloud Run Automated Deployments

This is pretty easy to setup, Google has some great documentation for this: [https://cloud.google.com/build/docs/automating-builds/build-repos-from-github](https://cloud.google.com/build/docs/automating-builds/build-repos-from-github)

In addition, we can also utilize Cloud Functions to setup continuous delivery to Cloud Run with each new Cloud Build using Pub/Sub. Follow this article for specific instructions: [https://medium.com/google-cloud/continuous-deployment-to-cloud-run-services-based-on-a-new-container-image-bccd776b7357](https://medium.com/google-cloud/continuous-deployment-to-cloud-run-services-based-on-a-new-container-image-bccd776b7357)

## Showing it off

Now we can add ENV variables directly to Cloud Run:

Or you can add them as tags with API or CLI requests like so (see bottom):

`"run",
"deploy",
`${serviceName}`,
"--image",
"gcr.io/GOOGLE_CLOUD_PROJECT/store",
"--region",
"us-west2",
"--allow-unauthenticated",
"--platform",
"managed",
"--update-env-vars",
`REACT_APP_FIREBASE_USER_ID=${userId},REACT_APP_STORE_LOGO=${logoUrl}``

Now when you visit your Cloud Run url your client will be able to access your env variables. 🥳