In this blog post, we will be deploying a containerized Elixir/Phoenix application to Google Cloud Run. Cloud Run is a managed compute platform that lets you run containers directly on top of Google’s scalable infrastructure. You may be asking yourself, why choose Cloud Run when there are so many serverless platforms? Well I’d say they’re all pretty similar but Cloud Run is one of the easiest to get started on with many benefits integrated. After a successful deployment, you get a fully-qualified domain URL, logging, metrics, and managed deployments. Cloud Run does have its caveats like any other serverless platform. The big one is that Cloud Run requires you to deploy a containerized application. While many of our workloads are containerized, there’s still plenty that are not. Ok–that’s enough about Cloud Run and its pros and cons. If you need more info on whether your specific workload will work on Cloud Run see the official GCP documentation. The goal of this post is to give a general overview of the steps required to get a functional phoenix application running on GCP Cloud Run.
Deployment Steps Overview
Let’s take a look at the steps required to get our application deployed.
Create Elixir/Phoenix application
Build Container and Publish to Registry
Deploy Containerized Application to Cloud Run
Inject Environment Variables and Secrets
Create Elixir Application
The first step is hopefully the easiest (or hardest): get an elixir/phoenix application. I’ll be using a generic Phoenix application generated using mix “mix phx.new test_app –no-ecto”. Mix is the build tool for working with Elixir projects. I’ve chosen not to include a database connection for brevity. I’ll leave adding a database connection as an exercise for the user.
Containerize The Application
Step 2 is to containerize our application. You can write your own Dockerfile, or maybe like me, prefer to generate one using the mix command “mix phx.gen.release –docker” command. I have found that the generated dockerfile works in most cases. It also has the added benefit of providing a multi-stage docker build that can be useful in a CI/CD pipeline for caching the builder layer of our application.
Build Container And Publish To Registry
With both of the first 2 steps completed, we can move on to building and publishing the container image to a registry. I’ll be using Google’s Artifact Registry in this blog, but Google’s Container Registry and Docker Hub are also supported.
See https://cloud.google.com/run/docs/deploying for more information on supported repositories.
First, we need to login to the project we’re working on using gcloud.
> gcloud auth login
> gcloud config set project PROJECT_ID
Next, we need to have the artifact registry API enabled for our project. This may take several minutes to complete.
gcloud services enable artifactregistry.googleapis.com
After we have enabled the artifact registry, we need to create a repository. I’ll be naming it `my-phoenix-app`.
gcloud artifacts repositories create my-phoenix-app \
Create request issued for: [my-phoenix-app]
Waiting for operation [projects/blogs-407517/locations/us-east1/operations/9b1174ed-0006-4804-ad11-e2fc792b05b8] to complete...done.
Created repository [my-phoenix-app].
Once created, grab the registry url. It will be in the structure of The URL has the shape
> gcloud artifacts repositories describe my-phoenix-app --location us-east1
Encryption: Google-managed key
Registry URL: us-east1-docker.pkg.dev/blogs-407517/my-phoenix-app
Repository Size: 0.000MB
To build the docker image, I’ll be using Cloud Build. (Using Cloud Build to build our image is not a requirement. You can build the image locally and push it to the registry). I’ll include a quick snippet on how to do that below. The only requirement for the image is for it to target the amd64 architecture. Images not targeting amd64 will fail to start in the Cloud Run environment.
Ok, let’s enable Cloud Build and use it to build our container.
> gcloud services enable cloudbuild.googleapis.com
Operation "operations/acf.p2-182047168193-22266cc4-2e7a-4fe4-8008-ab0a1ea2a0b9" finished successfully.
Create a Cloud Build job with your registry URL as the tag.
gcloud builds submit --tag
To build and create the docker image locally, you first need to configure the registry. Then, using docker, build the image and finally push it up. If you are building locally, make sure to target the amd64 architecture. You can read more about that in the official docker multi-platform documentation.
> gcloud auth configure-docker us-east1-docker.pkg.dev
> docker build . \
> docker push us-east1-docker.pkg.dev/blogs-407517/my-phoenix-app/phoenix-app
Deploy Containerized Application to Cloud Run
With our image built and published we can now deploy it to Cloud Run. To deploy our image we need to reference it using its registry and tag. For Phoenix applications, we are also required to pass in the SECRET_KEY_BASE environment variable, which is a required environment variable to start Phoenix applications. You’ll also see –allow-unauthenicated flag set, this tells Cloud Run that we want to expose our application to the internet. If you want to deploy an internal service you will not want to set this flag.
> gcloud run deploy phoenix-service --image us-east1-docker.pkg.dev/blogs-407517/my-phoenix-app/phoenix-app:latest --region us-east1 --allow-unauthenticated --set-env-vars "SECRET_KEY_BASE=va2g7x4g99VQWGqmpACxD9j9tFMfQMKRyzhA3ZkWExKISsZbn/Z+eB7GwUKCTQZC"
Deploying container to Cloud Run service [phoenix-service] in project [blogs-407517] region [us-east1]
✓ Deploying... Done.
✓ Creating Revision...
✓ Routing traffic...
✓ Setting IAM Policy...
Service [phoenix-service] revision [phoenix-service-00005-c8x] has been deployed and is serving 100 percent of traffic.
Service URL: https://phoenix-service-o3f2fulppq-ue.a.run.app
That’s it. With a successfully deployed application, it is now accessible using the Service URL, and we have logging and reporting metrics being reported.
In a couple of steps we can get a phoenix/elixir application running in Cloud Run. We get many of the benefits of serverless with our application for a relatively low level of complexity. Hopefully this has demonstrated how easy it is to deploy your containerized application to Cloud Run. This post has barely scratched the surface of Cloud Run’s capability. There’re plenty of avenues to expand on the basic service we deployed i.e. traffic splitting, rolling/gradual deployments, and custom domains. Cloud Run can be the middle group between using kubernetes or self-hosting your application. Thanks for reading and happy coding!