How To deploy to DigitalOcean app platform with managed database

took me days to figure this out (with the help of you guys in Slack) so logging this down for future me. make sure you read the whole thing first and then deploy. you may want to change the order that you do this.

1. Get a Dockerfile

I had one from fly launch already.

1.1 Change the CMD

i changed my CMD to be

CMD ["/bin/bash", "-c", "/app/bin/migrate && /app/bin/server"]

you can also define your migrate/server command in the DO UI settings, but since I want multiple servers this was easier.

2. Deploy a managed database

do this before the app, it just makes it easier. note that if you start with the app it will offer you to attach a development database that you can “upgrade later”, but at least for me it created a set of problems because DO’s state doesn’t update very fast. better start with what you want.

2.1 Create user and database

use the UI for this.

2.2 Change ownership

you can get the Connection String from Overview. then in bash use psql "CONNECTION_STRING" (with quotes) to get into the database server and then ALTER DATABASE your_db OWNER TO your_user; to change the owner.

3. Deploy app

connecting a git repo is easy enough, but getting the database connected is not so easy so read ahead. configuration (next steps) can be done at this stage or after you’ve already deployed.

3.1 Attach database

DO has this concept of attaching the database. It will create a DATABASE_URL env var for you (that you can change/remove), but more importantly it’ll make bindable env vars available like ${your_db.CA_CERT}.

3.2 Set env vars

Find the env vars for your app (and it’s not under App).

Here’s what you need:

DATABASE_URL=${your_db.DATABASE_URL}
DATABASE_HOSTNAME=${your_db.HOSTNAME}
DATABASE_CA_CERT=${your_db.CA_CERT}
SECRET_KEY_BASE=[mix phx.gen]
PHX_HOST=[your_domain]

3.3 Configuration in elixir

this took me the most amount of time. I’ll paste, then explain. the base is stock phoenix.

 config :boocs, Boocs.Repo,
    ssl: true,
    ssl_opts: [
      verify: :verify_peer,
      cacerts: [
        System.get_env("DATABASE_CA_CERT")
        |> then(fn pem ->
          [{_type, der, _info}] = :public_key.pem_decode(pem)
          der
        end)
      ],
      server_name_indication: System.get_env("DATABASE_HOSTNAME") |> to_charlist(),
      customize_hostname_check: [
        match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
      ]
    ],
    url: database_url,
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
    migration_timestamps: [type: :utc_datetime_usec],
    migration_lock: nil,
    queue_target: 5000

  # socket_options: maybe_ipv6

SSL is required and it took me a long time which exact commands were required for it to work properly.

queue_target and the preceding 3 values were an interesting one. my initial migrations kept failing with something about connection pool timing out. even with ~200 connections allowed on the database side it crashed with this vague error. upgrading the DB to a $1000 server worked, but it made no sense. ultimately queue_target is what fixed it and I guess it had to do with the app server being too fast with it’s commands and the DB not answering fast enough.

DO doesn’t support IPV6. as far as I can tell. even though the ipv6 setting from phoenix is a “maybe” then removing this line still fixed an error I was stuck on for hours. it’s probably got something to do with the fact that fly did use ipv6 and the Dockerfile is form there.

Done

I’m not a devops guy and i do wish i could have stuck with fly, but at least it’s done now and ultimately DO is running a great service so happy to give it a go.

happy to answer any questions.

7 Likes