Deploying a Django application with AWS Lambda and Cloudflare R2

Oskar Ablimit
4 min readMar 12, 2024

--

Is there any better motivation to keep coding than creating something for your lovely, cute daughter? Not any for me.

So, I have created a Django app to store all the cutest pictures of my daughter and successfully deployed it to AWS Lambda and Cloudflare R2.

What is this Django app about?

Here is a high-level introduction to the features.

  • Users need to sign in to access all the features.
login page

.Users can upload pictures from local storage to Cloudflare R2.

picture uploading page

.Users can view all the uploaded pictures in the app.( it can be deleted both from database and Cloudflare R2 and it can be downloaded with a full size)

pictures display page

.The app is hosted on AWS Lambda with Zappa.

.The picture URLs are stored in AWS RDS as the main database for my app.

.Static files (CSS/HTML, images) are hosted on Cloudflare R2.

How does architecture look like?

Here is a brief architecture about the whole project.

Brief architect introduction
brief architecture for the code parts and deployment parts

How did I deploy it?

  1. Change the default database from SQLite to PostgreSQL in RDS.

As Zappa requires an external database, SQLite needs to be replaced by an external database. In my case, I used AWS RDS PostgreSQL as my database. Add this to your `settings.py`:

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'F{NAME}',
'USER': 'F{USER}',
'PASSWORD': f'{PASSWORD}',
'HOST': f'{HOST}',
'PORT': 'f{PORT}',
}
}

Note:

. Use environment variables for credentials such as password and hostname (`pip3 install python-dotenv` and load dotenv with `load_dotenv()`, create a `.env` file in the root (project) directory, and store all credentials there).

  • Create a `.pipignore` file to add `.env` to ignore credentials when uploading to the remote repo with Git.
  • DB default name is postgres

2. Change storage default settings to Cloudflare R2 with the following codes:

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
STATIC_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_STORAGE_BUCKET_NAME = 'f{bucket-name}'
AWS_S3_ENDPOINT_URL = os.getenv('AWS_S3_ENDPOINT_URL')
AWS_S3_REGION_NAME = 'auto' # This is really important to define the region; otherwise, Django storage cannot use the R2 API.
AWS_S3_ACCESS_KEY_ID = os.getenv('AWS_S3_ACCESS_KEY_ID')
AWS_S3_SECRET_ACCESS_KEY = os.getenv('AWS_S3_SECRET_ACCESS_KEY')
AWS_S3_SIGNATURE_VERSION = 's3v4'
AWS_S3_CUSTOM_DOMAIN = os.getenv('custom_domain')
STATIC_URL = os.getenv('static_url')
STATIC_ROOT = os.getenv('root')

Note:

. Pip install boto3 to interact with Cloudflare R2 API (S3 compatible).

. Must include the R2 endpoint; otherwise, boto3 reads S3 endpoint and gets an error. You can find the R2 endpoint URL in the Cloudflare object dashboard.

.Must include `region_name`; otherwise, boto3 gets a regions error.

.You can get R2 Access Key ID and secret key from the R2 dashboard (R2 API token).

.You can get the custom domain from the R2 dashboard.

3. Create a virtual environment and install Zappa with `pip3 install zappa`.

Note:

.Make sure `pip3` is within the virtual environment; otherwise, the packages will be stored outside of the environment. You can check with `which pip3` (it should be within the virtual environment).

4.Run `zappa init`.

Note:

.It will ask for the name of API gateway and the name of S3 bucket where Lambda functions are stored.

5.IAM user creation.

- Create an IAM user in AWS and assign two basic permissions to the IAM: IAM full access power access.

  • Get the IAM credentials.

6.Configure AWS.

- `Pip3 install awscli`.

- It will ask for the AWS IAM ID and secret key.

7. Run `zappa build {Name of API gateway}`.

8.Set up a custom hostname for the API gateway.

As now, we only get the API link to access our project, we can set up a custom hostname in API gateway to map with the API. Here is how I did it:

- Download the SSL certificate info from Cloudflare.

- Import SSL info to AWS Certificate Manager.

- Create a custom hostname in AWS API Gateway.

- Select the imported SSL certificate.

- Create an API mapping (this will generate a mapped API domain).

- Go to Cloudflare, add the mapped API domain as a target to CNAME of a subdomain.

  • Add this domain to allowed hosts in `settings.py`.

Done! Now my Django pplication is successfully deployed to AWS Lambda with Zappa, and used Cloudflare R2 as the main storage, check it out here, https://zadiaphoto.oskarcode.com/

For a quick background, I am not a developer. I have always worked in tech, more on the tech sales side. I have an engineering background but never worked as a developer on a big project. I have taught myself Python and been doing small projects here and there for several years. You can check the projects I have finished [here](https://mycodeforever.com/). As I am not required to code in my day-to-day job, I am doing this purely for hobby and fun.

Hope this can be helpful.

--

--

Oskar Ablimit

Cybersecurity and Cloud Practitioner.GWU master's graduate in engineering of Cloud Computing, self-taught Python developer with expertise in Django and Pandas.