Phemeral Logo
Back to blog

Serverless Django

What's so special about running Django on serverless compute?

Published September 14, 2026

8 min read

Django is the granddaddy of Python web frameworks. And as long as Django has been around, generally speaking, it’s been deployed in a quite standardized way. At a high level, that usually involves spinning up a VPS on your favorite hyperscaler or PaaS, and then going through the rigmarole of setting up and connecting the ancillary bits (Postgres, Redis, static files, a reverse proxy, workers, etc.). In the past few years, a fancy new technology has gained traction and given us a new way, potentially a better (& cheaper?) way, to deploy/host our Django apps. That technology, of course, is serverless computing.

First off, what does serverless computing actually mean? It can help to understand it by contrasting it to the classical practice.

The classical way. Traditionally, one would run their application on a long-running server. You rent the full capacity of a server’s CPU, memory, disk, and network bandwidth at all times, regardless of how much you’re actually consuming. And if you think you might need more compute, you’ll need to either write and maintain autoscaling policies or manually scale the number of servers in and out. At the end of the day, this works. But, it can be laborious to get right, and usually not the most cost or time efficient solution.

The serverless way. Counterintuitively, serverless does not mean there are no servers. The basic meaning of serverless is that you as the user do not need to provision, manage, or scale servers directly; that stuff is done by the serverless hosting provider of your choice. You give your code to them, and they’ll run it for you. The way that serverless hosting providers run your apps is not just by plopping them on their own long-running servers. We should take some time to understand how they make that work.

Serverless hosting providers usually make things “serverless” by running your application in a bunch of tiny little VMs. When a serverless provider receives a request to your application, one of these tiny little VMs will be booted and run your application inside of it. And once your app sends the response, that VM will be shut down. When your application receives concurrent requests, they’ll spin up a VM for each one of those concurrent requests, so there could be a bunch of copies of your app running at the same time. Similarly, if your app is receiving no traffic, there won’t be any VMs running at all. The memory and CPU needs for each individual VM are quite low, since each is only handling one request! All in all, your app is hosted and will scale from using 0 compute to as much compute as you could ever need (without having to provision and manage servers or write any scaling policies). Pretty nifty.

Given that there’s no long-running server, billing for serverless is also a little different. Rather than renting out a large server for a block of time, you’re billed per-request. For each request, you’re billed for the exact amount of time that the associated VM has to stay alive for (which is until your app sends its response). Say that your app takes 200ms to respond to a request, it receives 10k requests a month, and the rate for compute is 10¢/hr.

200ms*10k =~30 mins. That would cost 5 cents a month.

Now, if serverless hosting is so great, then why isn’t every Django app using it? Well, there are some nuances. At its inception, serverless compute was offered in the form of serverless functions (like AWS Lambda), which are designed to execute short and usually event-driven tiny snippets of code. Individual serverless functions had memory and code size limits that were too low for running entire applications in them. Recently, these memory and code size limits have increased so we can fit our backends in them, but there is still the problem of cold starts.

When an incoming request to your app requires a VM to boot, that boot will take some time. The OS, the Python runtime, and your app need to come up! This is known as a cold start. Depending on the size of your app, what you’ve got going on in your app at startup, and the last time a request was made to your app, this cold start time can range anywhere from 500 milliseconds to over 5 seconds. Potentially waiting 5 seconds before your app even starts processing a request is not acceptable, 500ms isn’t great either. Especially when you pay this penalty for every concurrent request!

Okay, so if serverless has this problem, then why would anyone use it? Well, it can and has been solved. Through a lot of bit twiddling while developing our serverless hosting platform at Phemeral, we got cold starts for Python web apps below 50 ms (it takes longer for a network packet simply to travel between us-east-1 and us-west-1). And under concurrency or when an app has been recently used, often there are no cold starts at all.

Now, with cold starts and memory limits addressed, plus a basic understanding of serverless deployments, let’s shift from theory to practice. Concretely, how do we actually get a Django app hosted on serverless compute and what are some things we’ll need to watch out for?

For starters, pick your favorite serverless compute provider. Some options are: deploying on AWS Lambda using the Lambda web adapter/Zappa/Mangum, GCP’s Cloud Run product, or Cloudflare workers. Phemeral is specifically optimized to host Python web apps on serverless infra, so it’s quite fast (Re: cold starts). I recommend it, but do your research and make your own determination.

The specific process of getting your code running on these platforms varies quite a bit.

With Lambda, you’ll need to zip and put your code in S3, or containerize and store your container image on ECR. Also, make sure to wrap your code in something (Mangum, Lambda Web Adapter, Zappa) that translates between the Lambda context and ASGI/WSGI context. There is potentially a lot more AWS-specific configuration you could do.

Like other PaaSs, on Phemeral, you can deploy and get continuous deployment by connecting your git repo. In this case, your code does not need to be containerized, rather Phemeral creates its own build when you deploy your code. The specific build it creates is one of those things that results in really fast cold starts.

There are some things to note as you’re getting your Django project running on serverless compute.

First is the stateless nature of serverless hosting (basically, if you’re holding state in memory or on disk, don’t expect it to persist across requests). Your views should be stateless already, but if you’ve written a view that mutates and references things at the module level (like a global), understand those mutations might not be there at the next request. Another thing to watch out for is relying on state stored on disk (e.g., if you’re using SQLite in-process).

Second is database connections. You’ll want to make sure that you’re using a connection pooler in front of your database (this is because each request will have its own connection to your db, so under concurrency, you could easily max out your db’s max connections). Most DBaaS’s give this to you out of the box. A common setup is using Pgbouncer in front of Postgres. In that case, you’ll wanna disable server-side cursors for your Django project. The Django docs are a great resource on why and how to do that: here.

That broadly covers deploying the application process itself, but what about all the important ancillary bits? Imagine if your entire stack could run serverlessly end to end: the application, database, cache, queues, workers, etc. Every piece of your infra would independently scale to match its respective traffic, and you’d only pay for what you use. (Save time x N) + (Save money x N). 🙂

Some of those ancillary things are already "serverless" by nature. For example, people generally don’t run their own SMTP servers, they use an email service like Resend or SES that manages the sending infra for them. Object storage is also inherently serverless.

For some of the stateful stuff (database, cache, queue), serverless implementations might not seem possible. However, if the engine and storage layers for these are separated, we can make the engine serverless, and leave the storage stateful. For example, Phemeral offers managed serverless Postgres alongside its application hosting. AWS’s Aurora offers a serverless Postgres implementation as well. Cloudflare D1 gives you serverless SQLite. There are a ton of serverless NoSQL options too; one example is AWS’s DynamoDB.

For caching, sessions, message brokering, and the like, Upstash has a neat serverless Redis implementation. They also have something called QStash, which is essentially a Redis queue that can make calls to your app with retries and scheduling. It can be used as a broker for background work. In practice, you’d have a view defined for some background task, and queue up a call to that view’s URL when you want it to be run.

I opened by suggesting that deploying on serverless compute could be a good alternative to the traditional path. We looked under the hood of serverless orchestration (tiny VMs) to understand the benefits (scaling + lower costs)…and the gotchas (cold starts). We looked at what specifically to watch out for with Django (stateless design and db connection pooling). And finally, we mentioned a bunch of tools that could be used to make our entire stack serverless! Obviously, none of what I mentioned is required to host your Django project, it simply could make your life easier and save you some dough. Hopefully, you found it instructive, and you’re now hyped to try this stuff out on your own!