---
title: Deploying on fly.io
tags: docker, deployment, websockets
---

I spent some time today deploying [uMap](https://umap-project.org) to [fly.io](https://fly.io), using a `Dockerfile`. Here are some notes I took.

First, I had to install the `flyctl` command. On OSX, with `brew`, it's:

```bash
brew install flyctl
```

Fly is using docker containers under the hood, and provides some tools to help  create the files. In the case of uMap, I already had a `Dockerfile`, so it wasn't necessary.

## Creating an app

I created an app:

```bash
fly apps create
```

By answering the questions. It eventually generated a `fly.toml` file., that I actually rewrote to fit my needs:

```toml
app = 'umap'
primary_region = 'iad'
console_command = 'umap shell'

[build]
  dockerfile = "Dockerfile"

[[services]]
  internal_port = 8000
  protocol = 'tcp'
  processes = ["app"]

  [[services.ports]]
    handlers = ['http']
    port = 80
    force_https = true

  [[services.ports]]
    handlers = ['http', "tls"]
    port = 443

[[vm]]
  memory = '1gb'
  cpu_kind = 'shared'
  cpus = 1
```

The `services` section could be simplified by using the `http_service` section, but as I will need to configure websockets on a different port, it seems to be a better bet.
## Creating PostgreSQL / PostGIS database

I created a postgres app, that I named `postgres_umap` by answering the questions asked by this command:

```bash
flyctl postgres create
```

Then I had to activate the PostGIS extension, by connecting to the postgres cluster…

```bash
flyctl postgres connect -a postgres-umap
```

… and enter this:

```
CREATE EXTENSION postgis
```

At this stage, the machine crashed, and a `fly logs -a postgres-umap` told me that it was out of memory. I bumped the memory a bit with this:

```bash
# got the id of the machine with
fly machine status -a postgres-umap
# and then bumped the memory to 1024mb
fly machine update 3d8dd964a25978 --vm-memory 1024 --app postgres-umap
```

Redoing the `CREATE EXTENSION postgis;` worked this time.

Once this is done, I used this utility:

```bash
fly postgres attach postgres-umap --app umap
```

This created users, passwords and a database for me, and set the proper secrets for my app. Neat. 

I received an email telling me this costs money, so after initializing the postgis database, I scaled the instance down:

```bash
fly machine update 3d8dd964a25978 --vm-memory 256 --app postgres-umap
```

## Handling secrets

Additionally to environment variables (which are present in the `fly.toml` file), I also had to define some secrets, using `fly secrets`:

```bash
secrets set SECRET_KEY=S0_S3CR3T
```

At some point, I had to unset some of them with: 

```bash
secrets unset SECRET_KEY
```

I also added a few env vars:

```toml
[env]
  WEBSOCKET_URI = "wss://xxx.fly.dev:8001"
  WEBSOCKET_HOST = "0.0.0.0"
  WEBSOCKET_ENABLED = "True"
  UMAP_ALLOW_ANONYMOUS = "True"
```

## Deploying

With this in place, I could deploy these two machines with `fly deploy`.

## Adding support for WebSockets

This can change in the future, but for now, I have two servers, a WebSocket server listening on port `8001`, separate from the `uwsgi` server listening on `443` and `80`.

It is possible to define some specific commands that will replace the default `CMD` one of the `Dockerfile`. 

To do this, I used the `[processes]` section, and named the default one `django`, because it's running the django server with `uwsgi`:

```toml
[processes]
ws = "/venv/bin/python /venv/lib/python3.11/site-packages/umap/ws.py"
django = "/srv/umap/docker/entrypoint.sh"
```

… and added a new services definition:

```toml
[[services]]
    internal_port = 8001
    protocol = 'tcp'
    processes = ['ws']
    
    [[services.ports]]
      handlers = ['tls']
      port = 8001
```

Also, I'm not sure why I had to downgrade the number of processes:

```bash
fly scale count 1 --process-group=django
fly scale count 1 --process-group=ws
```

## Using a custom domain

I wanted this to be hosted on my domain, so I went with adding a `CNAME`, following [this documentation](https://fly.io/docs/networking/custom-domain/):

```bash
CNAME 	xxx 	xxx.fly.dev
```

And then generated the SSL certs:

```bash
fly certs add xxx.domain.tld
```

## Persisting files

Everything was working smoothly until I realize that the files weren't actually persisted. For the files to be persisted, I needed to add volumes in the toml file:

```toml
[mounts]
  source="umap_data"
  destination="/srv/umap/uploads"
```

And create the volumes with the cli:

```bash
fly volume create umap_data -r iad -n2
fly deploy
```
