I have a love-hate relationship with Digital Ocean's App Platform. On the one hand, it's flexible, relatively cheap and reliable. You can scale easily, deployments are automated and once everything is up and running I rarely have to login to check on it. It's got some nice dashboards and a handy command line tool
On the other hand, the UI is so confusing that I sometimes wonder if I'm the only person to ever have successfully launched a project on it. There's very little community information and the documentation feels like it was dropped on the floor and reshuffled.
So in the first of a few posts on Digital Ocean’s App Platform, I'm going to attempt to demystify it and go through some of the high-level concepts that are missing from their docs, hopefully helping you to launch projects without raising your blood pressure.
Bear in mind that this post is written with a Django REST API backend app in mind, where you'll likely be using a Postgres database, Redis key store for caching and a Celery queue for task management.
The App Platform is Digital Ocean's PAAS (platform as a service). In other words it's the Digital Ocean version of Heroku, Fly.io, Renderor AWS EBS It lets you run apps in the cloud with minimal configuration and oversight.
So what does an app on the App Platform look like? Well the first thing to realise is that the App Platform is designed to handle both frontend and backend apps. At its most simple, it's like Netlify or Vercel where you point your repo at it and it spits out a static app. Easy. For backend apps though it's a bit more complicated as you still need to bring your own database and storage. Thankfully Digital Ocean offers these as separate products in their ecosystem.
This makes the whole thing more expensive then running your own server, but with much fewer moving parts.
All in all, be prepared to spend at least $40-$60+/month to run a standard web application with a task queue.
- Spaces (S3-like storage) for media and static files = $5/month
- Hosted database instance (Postgres) = $15/month
- Hosted keystore instance (Redis) = $15/month
- App Platform app (x1 web service, x2 workers) = $25/month
So lets get into it. As I've mentioned, the high level documentation on the App Platform is awful. It does a really poor job at laying out the most important concepts of running an app on the App Platform and instead just jumps right into details.
Here are some of the buzzwords to come to terms with:
- App spec
- Cloud Native Buildpacks
It's very easy to get drawn into the Digital Ocean App Platform UI without realising that underneath everything, your app is actually just described by a simple text file called the "app spec".
The app spec is a
.yaml file that describes exactly how your application is configured. Think of it as the blue print or plans - if you accidentally deleted everything tomorrow, you'd be able to use the app spec to get everything back up and running in a few clicks.
A snippet of an example app spec file
If you jump straight into the DO App Platform web UI, it will try to help you create a new app spec without you realising and you'll have a bad time. Stop, take a breath and remember that everything boils down to an app spec YAML file on the App Platform.
The components making up a standard Django app
Any app on the App Platform is composed of a number of different components. There are a number of different component types that you can launch:
- Service components. This is our actual Django
appinstance that responds to requests. It sits behind a load balancer that Digital Ocean manages for you. You can scale these out from 1 instance to however many you need.
- Worker components. These are background services that don't respond to requests. For us, we need a
celery-beatworker to handle our async tasks. You can also scale these.
- Database components. We need to connect to our
postgresdatabase and our
rediskeystore. You actually create these databases elsewhere in the Digital Ocean ecosystem, the component itself just configures the connection to your database.
- Job components. These are once-off scripts that are run at certain times of the build and deployment process. For us, we configure an
updatejob to run our Django migrations and collect our static files every time we have a new deployment.
Depending on what you need for your app, you'll describe the required components in the app spec.
Where things begin to get confusing with the App Platform is when it comes to actually running a particular web component, for example our Django REST API app "service".
The first thing to understand is that, whether you realise it or not, all web apps on the App Platform are run as Docker containers. This means that Digital Ocean needs to be able to take your component (as described in your app spec) then build a Docker image and launch a container from that image.
If you're already familiar with Docker than this is great, you can simply supply your Dockerfile and configure your app spec to point directly to it. Digital Ocean will see this, build your image and deploy your container. It's all just Docker.
The problem here is that not everyone likes (or understands) Docker. So Digital Ocean gives you an alternative to make it easier for these underlying Docker images to be built.
In order to create apps with as little configuration as possible (for those that don't like Docker), Digital Ocean can guess what sort of app you are trying to deploy. To do this, it uses "Cloud Native Buildpacks". Essentially, this lets Digital Ocean figure out what sort of app you are trying to deploy by looking at your source code. It can then generate the app spec file automatically and create images and containers without any configuration needed on your end.
This seems like an obvious point to raise, but realise that Cloud Native Buildpacks is a library/technology that Digital Ocean uses, not you. In other words, you don't need to do anything to make use of buildpacks - the Digital Ocean infrastructure makes use of it transparently when reading your code.
This sounds great, but the downside is that this is incredibly magical and happens behind the scenes. When it works, it works seamlessly but when things go wrong they go spectacularly wrong.
My suggestion is to avoid relying on Cloud Native Buildpacks and instead provide a Docker file manually.
As we've mentioned, under the hood App Platform apps are essentially just
.yaml files called "app specs". Digital Ocean wants to make it easy to create apps without having to write out these files manually. The App Platform web UI is therefore an "app spec builder" that uses a number of interfaces to help you create a text file that describes your app.
This makes the whole experience very confusing as there is a tension between the UI and the underlying YAML file used to describe your app. Digital Ocean try to make it easy by giving you an interface for creating configuration files, but really this just makes it much more complicated.
- Focus on understanding the app spec.
- Make use of the
doctlcommand line tool to skip the web UI and create/update apps directly.
- But if using the web UI, manually edit the app spec directly instead of relying on the web interface itself.
Digital Ocean's App Platform interface gets in the way of a pretty powerful underlying infrastructure for deploying web applications. It will hopefully improve over time, but for the moment you'll need to grapple with a cumbersome and confusing web UI.
To make your life easy, only work on the underlying app spec text files directly (instead of using the web UI to configure your app), and bring your own Docker files instead of relying on Cloud Native Buildpacks.
In a later post I'll go through an actual example of deploying a Django app on the App Platform, so stay tuned.