Django Deployment - Postgres DBaaS
Tags: django, tutorial, cloud, database
Published: 2022-09-26 10:41:50

During my last attempt to get a Postgres server up and running, I realized again how time consuming it actually is. In addition to the installation of the Postgres server, the whole server has to be secured. This can take a lot of time. With a small vps with many users there is also the limited perfomrnace.

The alternative: **DBaaS** (Database-as-a-Service).

The advantages of a DBaaS are simple:

- Time saving (server management and installation)
- Security ( reduced risk and best practice)
- Data-Security (automatic backups)
- Scalability (fast and easy to scale down and up)

Providers I can recommend are:

- [Linode](https://www.linode.com/de/products/postgresql/)
- [DigitalOcean](https://www.digitalocean.com/products/managed-databases-postgresql)
- [Render](https://render.com/docs/databases)

The service from *fly.io* [Postgres on Fly](https://fly.io/docs/reference/postgres/) also looks very promising.

## Preparations

### Postgres

- login: `psql -U doadmin -h host -p port -d database`
- database creation: `CREATE DATABASE djangodb;`
- switch to the new db: `\c djangodb;`

After that we can create the new user with a secure password:

```
CREATE USER djangouser WITH PASSWORD 'securepassword';
```

For everything to work smoothly django recommends the following paramenter (see [django-docs](https://docs.djangoproject.com/en/4.1/ref/databases/#optimizing-postgresql-s-configuration)):

``` 
ALTER ROLE djangouser SET client_encoding TO 'utf8';

ALTER ROLE djangouser SET default_transaction_isolation TO 'read committed';

ALTER ROLE djangouser SET timezone TO 'UTC';
```

Finally we can change the privileges for our new user:

```
GRANT ALL PRIVILEGES ON DATABASE djangodb TO djangouser;
```

### Django

Here i decided to use [django-environ](https://github.com/joke2k/django-environ)'s `env.db()` for the `DATABASE_URL`.

``` python

import os
from pathlib import Path
import environ

BASE_DIR = Path(__file__).resolve().parent.parent

env = environ.Env()
env_file = os.path.join(BASE_DIR, ".env")
if os.path.isfile(env_file):
    env.read_env(env_file)

DATABASE_URL = env.str("DATABASE_URL", default=False)

if DATABASE_URL:
    DATABASES = {"default": env.db()}
else:
    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.sqlite3",
            "NAME": BASE_DIR / "db.sqlite3",
        }
    }

```

**Note about DigitalOcean's App Platform:**

If we use DigitalOcean's App Platform, we can skip the dynamic variable `${dbname.DATABASE_URL}` as `DATABASE_URL`. This has always led to an empty database for me with a redeploy. Even if I created a database via DO's GUI before, and changed the variable accordingly.

To ensure that our data is also available after a redeploy in the future, we can use the *Postgres-String* as variable (with encryption on).

## Conclusion

(in connection with DO's Postgres DBaaS)

I am very happy with the performance so far. If I need connection pooling (via PgBouncer) later, I can add it quickly and easily via DO's GUI. The price is a bit expensive for a single and smaller project, but quickly pays off when another project is added. With DigitalOcean the fun starts at 15€ and 3 databases (1GB Memory, 1 vCPU, 15GB Disk). 

---

Thanks for reading.

Originally published at [rburkhardt.com](https://rburkhardt.com/)
Feel free to subscribe to my [RSS](https://rburkhardt.com/feed/) and connect on [Twitter](https://twitter.com/rob_burkhardt) or [Github](https://github.com/rob32)