Deploying stateful applications to Cloud Run like Django involves integrating services to interact with each other to form a cohesive project.
This tutorial assumes that you're familiar with Django web development. If you're new to Django development, it's a good idea to work through writing your first Django app before continuing.
While this tutorial demonstrates Django specifically, you can use this deployment process with other Django-based frameworks, such as Wagtail and Django CMS.
This tutorial uses Django 5, which requires at least Python 3.10.
Objectives
In this tutorial, you will:
- Create and connect a Cloud SQL database.
- Create and use Secret Manager secret values.
Deploy a Django app to Cloud Run.
Host static files on Cloud Storage.
Use Cloud Build to automate deployment.
Costs
In this document, you use the following billable components of Google Cloud:
To generate a cost estimate based on your projected usage,
use the pricing calculator.
Before you begin
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Cloud Run, Cloud SQL, Cloud Build, Secret Manager, Artifact Registry, and Compute Engine APIs.
- Install the Google Cloud CLI.
-
To initialize the gcloud CLI, run the following command:
gcloud init
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Cloud Run, Cloud SQL, Cloud Build, Secret Manager, Artifact Registry, and Compute Engine APIs.
- Install the Google Cloud CLI.
-
To initialize the gcloud CLI, run the following command:
gcloud init
- Ensure sufficient permissions are available to the account used for this tutorial.
Prepare your environment
Clone a sample app
The code for the Django sample app is in the GoogleCloudPlatform/python-docs-samples repository on GitHub.
You can either download the sample as a ZIP file and extract it or clone the repository to your local machine:
git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
Go to the directory that contains the sample code:
Linux/macOS
cd python-docs-samples/run/django
Windows
cd python-docs-samples\run\django
Confirm your Python setup
This tutorial relies on Python to run the sample application on your machine. The sample code also requires installing dependencies
For more details, refer to the Python development environment guide.
Confirm your Python is at least version 3.10.
python -V
You should see
Python 3.10.0
or higher.Create a Python virtual environment and install dependencies:
Linux/macOS
python -m venv venv source venv/bin/activate pip install --upgrade pip pip install -r requirements.txt
Windows
python -m venv venv venv\scripts\activate pip install --upgrade pip pip install -r requirements.txt
Download Cloud SQL Auth Proxy to connect to Cloud SQL from your local machine
When deployed, your app uses the Cloud SQL Auth Proxy that is built into the Cloud Run environment to communicate with your Cloud SQL instance. However, to test your app locally, you must install and use a local copy of the proxy in your development environment. For more details, refer to the Cloud SQL Auth Proxy guide.
The Cloud SQL Auth Proxy uses the Cloud SQL API to interact with your SQL instance. To do this, it requires application authentication through the gcloud CLI.
Authenticate and acquire credentials for the API:
gcloud auth application-default login
Download and install the Cloud SQL Auth Proxy to your local machine.
Linux 64-bit
- Download the Cloud SQL Auth Proxy:
curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.0/cloud-sql-proxy.linux.amd64
- Make the Cloud SQL Auth Proxy executable:
chmod +x cloud-sql-proxy
Linux 32-bit
- Download the Cloud SQL Auth Proxy:
curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.0/cloud-sql-proxy.linux.386
- If the
curl
command is not found, runsudo apt install curl
and repeat the download command. - Make the Cloud SQL Auth Proxy executable:
chmod +x cloud-sql-proxy
macOS 64-bit
- Download the Cloud SQL Auth Proxy:
curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.0/cloud-sql-proxy.darwin.amd64
- Make the Cloud SQL Auth Proxy executable:
chmod +x cloud-sql-proxy
Mac M1
- Download the Cloud SQL Auth Proxy:
curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.0/cloud-sql-proxy.darwin.arm64
- Make the Cloud SQL Auth Proxy executable:
chmod +x cloud-sql-proxy
Windows 64-bit
Right-click https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.0/cloud-sql-proxy.x64.exe and select Save Link As to download the Cloud SQL Auth Proxy. Rename the file tocloud-sql-proxy.exe
.Windows 32-bit
Right-click https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.0/cloud-sql-proxy.x86.exe and select Save Link As to download the Cloud SQL Auth Proxy. Rename the file tocloud-sql-proxy.exe
.Cloud SQL Auth Proxy Docker image
The Cloud SQL Auth Proxy has different container images, such as
distroless
,alpine
, andbuster
. The default Cloud SQL Auth Proxy container image usesdistroless
, which contains no shell. If you need a shell or related tools, then download an image based onalpine
orbuster
. For more information, see Cloud SQL Auth Proxy Container Images.You can pull the latest image to your local machine using Docker by using the following command:
docker pull gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.0
Other OS
For other operating systems not included here, you can compile the Cloud SQL Auth Proxy from source.You can choose to move the download to somewhere common, such as a location on your
PATH
, or your home directory. If you choose to do this, when you start the Cloud SQL Auth Proxy later on in the tutorial, remember to reference your chosen location when usingcloud-sql-proxy
commands.- Download the Cloud SQL Auth Proxy:
Create backing services
This tutorial uses several Google Cloud services to provide the database, media storage, and secret storage that support the deployed Django project. These services are deployed in a specific region. For efficiency between services, all services should be deployed in the same region. For more information about the closest region to you, see Products available by region.
This tutorial uses the integrated static asset hosting mechanisms in Cloud Run.Set up a Cloud SQL for PostgreSQL instance
Django officially supports multiple relational databases, but offers the most support for PostgreSQL. PostgreSQL is supported by Cloud SQL, so this tutorial chooses to use that type of database.
The following section describes the creation of a PostgreSQL instance, database, and database user for the app.
Create the PostgreSQL instance:
Console
In the Google Cloud console, go to the Cloud SQL Instances page.
Click Create Instance.
Click Choose PostgreSQL.
For SQL Edition, choose "Enterprise".
For Edition Preset, choose "Sandbox".
In the Instance ID field, enter
INSTANCE_NAME
.Enter a password for the postgres user.
Keep the default values for the other fields.
Click Create Instance.
It takes a few minutes for the instance to be ready for use.
gcloud
Create the PostgreSQL instance:
gcloud sql instances create INSTANCE_NAME \ --project PROJECT_ID \ --database-version POSTGRES_16 \ --tier db-n1-standard-2 \ --region REGION
Replace the following:
INSTANCE_NAME
: the Cloud SQL instance namePROJECT_ID
: the Google Cloud project IDREGION
: the Google Cloud region
It takes a few minutes to create the instance and for it to be ready for use.
Within the created instance, create a database:
Console
- Within your instance page, go to the Databases tab.
- Click Create database.
- In the Database Name dialog, enter
DATABASE_NAME
. - Click Create.
gcloud
Create the database within the recently created instance:
gcloud sql databases create DATABASE_NAME \ --instance INSTANCE_NAME
Replace
DATABASE_NAME
with a name for the database inside the instance.
Create a database user:
Console
- Within your instance page, go to the Users tab.
- Click Add User Account.
- In the Choose how to authenticate dialog under "Built-in Authentication":
- Enter the username
DATABASE_USERNAME
. - Enter the password
DATABASE_PASSWORD
- Click Add.
gcloud
Create the user within the recently created instance:
gcloud sql users create DATABASE_USERNAME \ --instance INSTANCE_NAME \ --password DATABASE_PASSWORD
Replace
PASSWORD
with a secure password.
Set up Artifact Registry
Use Artifact Registry to create a registry to store your container image.
Console
In the Google Cloud console, go to the Artifact Registry page.
Click Create Repository.
Enter the following:
- For Name, enter "cloud-run-source-deploy".
- For Format, select "Docker".
- For Region, select REGION.
Keep the default values for the other fields.
Click Create.
gcloud
- Create an Artifact Registry:
gcloud artifacts repositories create cloud-run-source-deploy \
--repository-format docker \
--location REGION
Set up a Cloud Storage bucket
You can store Django's included static assets, as well as user-uploaded media,
in highly-available object storage using Cloud Storage.
The django-storages[google]
package handles
Django's interaction with this storage backend.
Console
In the Google Cloud console, go to the Cloud Storage page.
Click Create Bucket.
Enter the following (clicking "Continue" where needed):
- For Name, enter "PROJECT_ID_MEDIA_BUCKET".
- For Region, select REGION.
- For Storage Class, keep the defaults.
- For Prevent public access, clear "Enforce public access prevention".
- For Access Control, select "Fine-grained".
Keep the default values for the other fields.
Click Create.
gcloud
Create a Cloud Storage bucket:
gcloud storage buckets create gs://PROJECT_ID_MEDIA_BUCKET --location=REGION
Replace
MEDIA_BUCKET
with a suffix for the media bucket. Combined with the project ID, this creates a unique bucket name.
Store secret values in Secret Manager
Now that the backing services are configured, Django needs information about these services. Instead of putting these values directly into the Django source code, this tutorial uses Secret Manager to store this information securely.
Create Django environment file as a Secret Manager secret
You store the settings required to start Django in a secured env file.
The sample app uses the Secret Manager API to retrieve the secret
value, and the django-environ
package to load the values into the Django environment. The secret is configured
to be accessible by Cloud Run and Cloud Build.
Create a file called
.env
, defining the database connection string, the media bucket name, and a newSECRET_KEY
value:echo DATABASE_URL=postgres://DATABASE_USERNAME:DATABASE_PASSWORD@//cloudsql/PROJECT_ID:REGION:INSTANCE_NAME/DATABASE_NAME > .env echo GS_BUCKET_NAME=PROJECT_ID_MEDIA_BUCKET >> .env echo SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n1) >> .env
Store the secret in Secret Manager:
Console
In the Google Cloud console, go to the Secret Manager page.
Click Create secret
In the Name field, enter
django_settings
.In the Secret value dialog, paste the contents of your
.env
file.Click Create secret.
Delete the local file to prevent local setting overrides.
gcloud
Create a new secret,
django_settings
, with the value of the.env
file:gcloud secrets create django_settings --data-file .env
Delete the local file to prevent local setting overrides:
rm .env
Configure access to the secret:
Console
In Secret: django_settings, note the project number:
projects/PROJECTNUM/secrets/django_settings
Click the Permissions tab.
Click Grant access.
In the New Members field, enter
PROJECTNUM-compute@developer.gserviceaccount.com
, and then pressEnter
.In the Role drop-down menu, select Secret Manager Secret Accessor.
Click Save.
gcloud
1. Get the value of the Project Number (
PROJECTNUM
):gcloud projects describe PROJECT_ID --format='value(projectNumber)'
Grant access to the secret to the Cloud Run service account:
gcloud secrets add-iam-policy-binding django_settings \ --member serviceAccount:PROJECTNUM-compute@developer.gserviceaccount.com \ --role roles/secretmanager.secretAccessor
In the output, confirm that
bindings
lists the new service account.
Create secret for Django's admin password
The Django admin user is normally created by running the interactive management command createsuperuser
.
This tutorial uses a data migration to create the admin user, retrieving the admin password from Secret Manager.
Console
- In the Google Cloud console, go to the Secret Manager page.
Click Create secret.
In the Name field, enter
superuser_password
.In the Secret value field, enter a random, unique password.
Click Create secret.
In Details for
superuser_password
, make a note of the project number (projects/PROJECTNUM/secrets/superuser_password
).Click the Permissions tab.
Click Add.
In the New Members field, enter
PROJECTNUM-compute@developer.gserviceaccount.com
, and then pressEnter
.In the Role drop-down menu, select Secret Manager Secret Accessor.
Click Save
gcloud
Create a new secret,
superuser_password
, from a randomly generated password:echo -n "$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 30 | head -n1)" | gcloud secrets create superuser_password --data-file -
Grant access to the secret to Cloud Build:
gcloud secrets add-iam-policy-binding superuser_password \ --member serviceAccount:PROJECTNUM-compute@developer.gserviceaccount.com \ --role roles/secretmanager.secretAccessor
In the output, confirm that
bindings
lists only the Cloud Build as a member.
Run the app on your local computer
With the backing services configured, you can now run the app on your computer. This setup allows for local development, and applying database migrations. Note that database migrations are also applied in Cloud Build, but you will need to have this local setup in order to makemigrations
.
In a separate terminal, start the Cloud SQL Auth Proxy:
Linux/macOS
./cloud-sql-proxy PROJECT_ID:REGION:INSTANCE_NAME
Windows
cloud-sql-proxy.exe PROJECT_ID:REGION:INSTANCE_NAME
This step establishes a connection from your local computer to your Cloud SQL instance for local testing purposes. Keep the Cloud SQL Auth Proxy running the entire time you test your app locally. Running this process in a separate terminal allows you to keep working while this process runs.
In the original terminal, set the Project ID locally (used by the Secret Manager API):
Linux/macOS
export GOOGLE_CLOUD_PROJECT=PROJECT_ID
Windows
set GOOGLE_CLOUD_PROJECT=PROJECT_ID
Set an environment variable to indicate you are using Cloud SQL Auth Proxy (this value is recognised in the code):
Linux/macOS
export USE_CLOUD_SQL_AUTH_PROXY=true
Windows
set USE_CLOUD_SQL_AUTH_PROXY=true
Run the Django migrations to set up your models and assets:
python manage.py makemigrations python manage.py makemigrations polls python manage.py migrate python manage.py collectstatic
Start the Django web server:
python manage.py runserver 8080
In your browser, go to http://localhost:8080.
If you are in Cloud Shell, click the Web Preview button, and select Preview on port 8080.
The page displays the following text: "Hello, world. You're at the polls index." The Django web server running on your computer delivers the sample app pages.
Press
Ctrl
/Cmd
+C
to stop the local web server.
Deploy the app to Cloud Run
With the backing services setup, you can now deploy the Cloud Run service.
Using the supplied
cloudmigrate.yaml
, use Cloud Build to build the image, run the database migrations, and populate the static assets:gcloud builds submit --config cloudmigrate.yaml \ --substitutions _INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION
This first build takes a few minutes to complete.
When the build is successful, deploy the Cloud Run service for the first time, setting the service region, base image, and connected Cloud SQL instance:
gcloud run deploy polls-service \ --region REGION \ --image REGION-docker.pkg.dev/PROJECT_ID/cloud-run-source-deploy/polls-service \ --add-cloudsql-instances PROJECT_ID:REGION:INSTANCE_NAME \ --allow-unauthenticated
You should see output that shows the deployment succeeded, with a service URL:
Service [polls-service] revision [polls-service-00001-tug] has been deployed and is serving 100 percent of traffic. Service URL: https://polls-service-PROJECT_ID.REGION.run.app
Update the service to the service URLs as an environment variable.
CLOUDRUN_SERVICE_URLS=$(gcloud run services describe django-cloudrun \ --region $REGION \ --format "value(metadata.annotations[\"run.googleapis.com/urls\"])" | tr -d '"[]') gcloud run services update polls-service \ --region REGION \ --update-env-vars "^##^CLOUDRUN_SERVICE_URLS=$CLOUDRUN_SERVICE_URLS"
To see the deployed service, go to the service URL.
To log into the Django admin, append
/admin
to the URL, and login with the usernameadmin
, and the password set earlier.To retrieve the superuser password from Secret Manager:
gcloud secrets versions access latest --secret superuser_password && echo ""
Updating the application
While the initial provisioning and deployment steps were complex, making updates is a simpler process:
Run the Cloud Build build and migration script:
gcloud builds submit --config cloudmigrate.yaml \ --substitutions _INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION
Deploy the service, specifying only the region and image:
gcloud run deploy polls-service \ --region REGION \ --image REGION-docker.pkg.dev/PROJECT_ID/cloud-run-source-deploy/polls-service
Configuring for production
You now have a working Django deployment, but there are further steps you can take to ensure that your application is production-ready.
Disable debugging
Confirm that the DEBUG
variable in mysite/settings.py
is set to False
. This
will prevent detailed error pages from being displayed to the user, which can leak information about the configurations.
Limit the database user privileges
Any users that are created by using Cloud SQL have the privileges associated with the
cloudsqlsuperuser
role:
CREATEROLE
, CREATEDB
, and LOGIN
.
To prevent the Django database user from having these permissions, manually create the user in PostgreSQL. You will need to have the psql
interactive terminal installed, or use Cloud Shell which has this tool pre-installed.
Console
-
In the Google Cloud console, activate Cloud Shell.
In Cloud Shell, use the built-in terminal to connect to your
INSTANCE_NAME
instance:gcloud sql connect INSTANCE_NAME --user postgres
Enter the postgres user password.
You are now using
psql
. You should see thepostgres=>
prompt.Create a user:
CREATE USER DATABASE_USERNAME WITH PASSWORD 'DATABASE_PASSWORD';
Replace
PASSWORD
with a random, unique password.Grant full rights on the new database to the new user:
GRANT ALL PRIVILEGES ON DATABASE DATABASE_NAME TO DATABASE_USERNAME;
Exit
psql
:\q
gcloud
Start a connection to the SQL instance:
gcloud sql connect INSTANCE_NAME --user postgres
Replace
INSTANCE_NAME
with the created Cloud SQL instance.Enter the postgres user password.
You are now using
psql
. You should see thepostgres=>
prompt.Create a user:
CREATE USER DATABASE_USERNAME WITH PASSWORD 'DATABASE_PASSWORD';
Grant full rights on the new database to the new user:
GRANT ALL PRIVILEGES ON DATABASE DATABASE_NAME TO DATABASE_USERNAME;
Exit
psql
:\q
Setting minimum permissions
By default, this service is deployed with the default compute service account. However, in some cases, using the default service account can provide too many permissions. If you want to be more restrictive, you need to create your own service account and assign only the permissions that are required by your service. The permissions required can vary from service to service, depending on the resources used by a particular service.
The minimum project roles required by this service are the following:
- Cloud Run Invoker
- Cloud SQL Client
- Storage Admin, on the media bucket
- Secret Manager Accessor, on the Django settings secret. (Access to the Django admin secret is not required by the service itself.)
To create a service account with the required permissions, and assign it to the service, run the following:
In the gcloud CLI, create a service account with the required roles:
gcloud iam service-accounts create polls-service-account SERVICE_ACCOUNT=polls-service-account@PROJECT_ID.iam.gserviceaccount.com # Cloud SQL Client gcloud projects add-iam-policy-binding PROJECT_ID \ --member serviceAccount:${SERVICE_ACCOUNT} \ --role roles/cloudsql.client # Storage Admin, on the media bucket gcloud storage buckets add-iam-policy-binding gs://MEDIA_BUCKET \ --member=serviceAccount:${SERVICE_ACCOUNT} \ --role=roles/storage.objectAdmin # Secret Accessor, on the Django settings secret. gcloud secrets add-iam-policy-binding django_settings \ --member serviceAccount:${SERVICE_ACCOUNT} \ --role roles/secretmanager.secretAccessor # Secret Accessor, on the Django super user password. gcloud secrets add-iam-policy-binding superuser_password \ --member serviceAccount:${SERVICE_ACCOUNT} \ --role roles/secretmanager.secretAccessor
Deploy the service, associating it with the new service account:
gcloud run services update polls-service \ --region REGION \ --service-account ${SERVICE_ACCOUNT}
Update the Cloud Run jobs in
cloudmigrate.yaml
to also have this--service-account
setting.
Understand the code
Sample application
The Django sample app was created using standard Django tooling. The following commands create the project and the polls app:
django-admin startproject mysite
python manage.py startapp polls
The base views, models, and route configurations were copied from Writing your first Django app (Part 1 and Part 2).
Secrets from Secret Manager
The settings.py
file contains code that uses the Secret Manager Python API to retrieve the latest version of the named secret, and pull it into the environment (using django-environ
):
The secret is used to store multiple secret values to reduce the number of different secrets that needed to be configured.
CSRF configurations
Django has built-in protection against Cross Site Request Forgery (CSRF). Starting in Django 4.0, changes to the way this works mean that it's important to tell Django what it's hosted URL is, so it can offer the best protections for users submitting data.
You supply the app's URL as an environment variable in the settings.py
file
This is the value that Django uses for the relevant settings.
Local secret overrides
If a .env
file is found on the local filesystem, it is used instead of the value from Secret Manager. Creating a .env
file locally can help with local testing (e.g. local development against a SQLite database, or other local settings).
Database connection
The settings.py
file contains the configuration for your SQL database. It uses the env.db()
helper
from django-environ
to load the connection string set in DATABASE_URL
into the DATABASES
setting.
When running the application locally and using the Cloud SQL Auth Proxy to access the
hosted database, the USE_CLOUD_SQL_AUTH_PROXY
flag adjusts the database
settings to use the proxy.
Cloud-stored static
The settings.py
file also uses django-storages
to integrate the Cloud Storage media bucket directly into the project:
Automation with Cloud Build
The cloudmigrate.yaml
handles all the steps required to create a new container
image, and use it to apply database migrations.
This tutorial opts to use buildpacks to build the container image, and
custom entrypoints in the
Procfile
to handle
management commands. These can then be referenced by Cloud Run jobs to
perform the commands.
Substitution variables are used in this configuration.
Superuser creation with Cloud Run Jobs
The Django management command
createsuperuser
can be run non-interactively by setting DJANGO_SUPERUSER_EMAIL
and DJANGO_SUPERUSER_PASSWORD
.
This tutorial opts to use Cloud Run Jobs to run this command. This
is done by adding a custom entry into the Procfile
which runs createsuperuser
.
Clean up
To avoid incurring charges to your Google Cloud account for the resources used in this tutorial, either delete the project that contains the resources, or keep the project and delete the individual resources.
Delete the project
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
What's next
- Learn how to configure PostgreSQL for production
- Learn more about Django on Google Cloud