Angular with Cloud Storage

Serving an Angular App’s Static Files from Google Cloud Storage (Part 15)

In this blog post, you’ll learn how to serve an Angular app’s static files from Google Cloud Storage.

This post is part of the Dockerized Django Back-end API with Angular Front-end Tutorial. Check out all the parts of the tutorial there.

In the last part of the tutorial, we’ve learned how to deploy a Docker container on a VM in Google Cloud Platform. In this blog post, we’ll see how we can upload our Angular app to Google Cloud Storage and access it from our production Compute Engine VM.

Why Serve Static Content Separately?

In production environments, it’s generally best practice to serve static content separately from your app, either in GCP or using a third-party Content Delivery Network (CDN).

The reason for this is that serving static files from the app server itself can take up resources that would be better used for dynamic requests.

It’s better from a resource management point of view if the app server handles dynamic requests exclusively.

As a plus on the GCP side of things, it’s worth mentioning that Cloud Storage works as a CDN. By default, any publicly readable object is cached in the global Cloud Storage network.


This tutorial is based on the documentation notes for Cloud Storage, where it’s described how to host static assets for a website.

The notes and this tutorial too assume you have registered a domain name. For simplicity, we’ll use the domain, but replace this as appropriate with your own domain.

If you got to the stage where you’re ready to deploy your app in production, then you’ve probably already registered a domain name, so you’re good to go.

If not, you can still use direct links to Cloud Storage objects, but the typical pattern is to have your own domain or subdomain with a DNS record pointing to Cloud Storage.

Pointing the Root Domain Name to the Google Cloud VM

First of all, we need to point our domain name to the Compute Engine VM where we’re running the Django Docker container.

Assigning a static external IP address to the VM

Before pointing the root domain name to our VM, we first need to ensure that the VM has a static external IP address.

By default, the Compute Engine VMs have ephemeral IP addresses, which means that the IP address of the VM can change. If we’re working with domain names, then we need to ensure the IP address of the VM remains the same.

If you have already reserved a static external IP address, then do the following:

  1. Go to the VM instances page.
  2. Click the instance on which you’re running the Django container.
  3. Click Edit.
  4. Under External IP select a static external IP address if you’ve already reserved one.

If your instance currently has an ephemeral external IP address, you can promote the ephemeral external IP address to a static external IP address by doing the following:

  1. Go to the External IP addresses page in the GCP console.
  2. In the Type column, change the address type to Static for the IP address you want to promote.
  3. Provide a name for the new static IP address and click Reserve.
  4. Optional: check that the static external IP address has been reserved by executing:
$ gcloud compute addresses list

Create an A record pointing traffic to the VM

Now we’re ready to point the root domain to the VM’s IP address.

In your domain registrar panel, just create an A record that points to the static IP address you’ve assigned in the previous section.

Serving Files from Google Cloud Storage

We’ll now proceed to deal with the static files.

Creating a CNAME record pointing to Cloud Storage

Again from the domain registrar, create a CNAME record to redirect traffic from to

Creating a bucket

Next, create a Cloud Storage Bucket for with the same name.

  1. Open the Cloud Storage Browser.
  2. Click Create bucket.
  3. Add details of your choice, remembering to choose for the Name.
  4. Click Create.

Uploading the Angular Static Files

To copy files to the bucket, you can use the gsutil cp command. For example:

$ gsutil cp Desktop/myfile.txt gs://

For copying a large number of files, use gsutil rsync, optionally with the -R option to recursively copy directory trees. For example, to synchronise a local directory named mydir, use:

$ gsutil rsync -R mydir gs://

Proceed to upload the Angular app’s static files in gs:// This location would then be accessible from

In the next commands, I’m uploading the Angular production (NOT development!) files from the /Users/dragos/static_angular directory on my computer. These are the same production files copied from the Angular (i.e. ng) container in part 10 of the tutorial, where we prepared our Angular app for production.

Make the Angular Files Publicly Accessible

Now that the files are uploaded, let’s make them public.

  1. Select your bucket from the Cloud Storage Browser.
  2. Go the Permissions tab.
  3. Click Add members.
  4. In the Members field, enter allUsers.
  5. Select Role, then select Storage, and click the Storage Object Viewer option.
  6. Click Add.

The Angular app’s files are now publicly accessible.

Serving the Angular App with uWSGI

At this point, the Angular files are on Cloud Storage, but how can the user actually get to them?

Well, it’s a simple matter of serving the Single-Page Application (SPA) from an index.html file when the user accesses the homepage of the domain.


We have to do this from uWSGI, the app server that’s serving our Django REST API.

Here’s how you should modify your uwsgi.ini configuration file in django/uwsgi:

Let’s explain the new additions:

  • check-static = /code/static_files – points uWSGI to a directory of static files that it should serve. This directory will contain the index.html file, which will essentially be our SPA.
  • static-index = index.html – tells uWSGI to serve index.html when a request for a directory is made; the default is to return 404, because uWSGI doesn’t have built-in directory indexing.
  • route-if = startswith:${REQUEST_URI};/api continue: – if the REQUEST_URI starts with /api, satisfy the request; the requests intended for the API will end up matching here.
  • route-if-not = exists:/code/static_files${PATH_INFO} static:/code/static_files/index.html – if the requested file does not exist in /code/static_files, serve code/static_files/index.html instead, i.e. the Angular app.

Now we should be properly serving the index.html file.

Create a static_files directory within the django directory. Now create the index.html file in the static_files directory as below:

This is essentially the default index.html file that gets created when you make a new Angular app with the Angular CLI, but we’re now pointing to the JavaScript files uploaded to the Cloud Storage bucket.

Replace in index.html with your own domain name.

Now that we’re using the app from the Google VM, we need to add the production Auth0 URLs in the Auth0 Dashboard as mentioned in part 7 of this tutorial.

So go ahead and go to the Auth0 dashboard, go to your application, and in the Settings tab, add the corresponding URLs containing the VM’s IP to the Allowed Callback URL and Allowed Logout URLs sections.

To get the code to this stage (and final), see my git commit or use:

$ git checkout v1.20


In this part of the tutorial, we’ve learned how to serve an Angular’s app static files from Google Cloud Storage. We’ve covered uploading the Angular production files to a Cloud Storage bucket. We’ve also seen how to serve the index.html of the Angular app using uWSGI.

In the next part of the tutorial…

There’s no next part! We’re finished!


Hey, if this was useful to you, leave a comment and let me know!

If it was crap, leave a comment and let me know!

Credit: For this part of the tutorial, I’ve used the following resources:

About the Author Dragos Stanciu

follow me on:


Like this article? Stay updated by subscribing to my weekly newsletter:

Leave a Comment:

Add Your Reply