Deploying an Elixir Release using Docker on DigitalOcean

Deploying an Elixir Release using Docker on DigitalOcean

Deploy a Phoenix 1.6 app using Elixir Releases and Docker on DigitalOcean

Last time we created a Docker image and ran a container based on it. Now we'll deploy our Elixir Release Docker image in DigitalOcean.

There are several approaches to this:

  • Let DigitalOcean access directly your GitHub or GitLab repository
  • Put the image in Docker public registry where DigitalOcean can fetch it
  • Put the image in DigitalOcean own Container Registry

In this article we'll use the first approach

Create a repository

I'll show how to do it in GitHub but should be similarly easy in GitLab


Create a new repository

Create new repository

push the code:

git remote add origin
git branch -M main
git push -u origin main

Prepare to deploy to DigitalOcean

DigitalOcean requires some changes to our code in order to correctly connect to the DB from our Docker application.

So far we have been establishing a plain, unencrypted connections between the application and the database. This won't work for DigitalOcean as they strictly enforce SSL when connecting to the database.

In our case, that means that we need to enable SSL on the application side every time we open a connection to the database.

First thing is to configure the Repo to use ssl. Open runtime.ex and uncomment the ssl: true line on the Repo section:

  config :saturn, Saturn.Repo,
    ssl: true,
    # socket_options: [:inet6],
    url: database_url,
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")

This will enable SSL for all the connections in the pool that the repository uses to connect to the database.

We need to change the Saturn.Release module that we use to run the migrations, because that also opens a connection to the database and we need it to start the ssl application at the beginning.

Change the load_app function in the Saturn.Release module to be like this:

  defp load_app do

This will ensure the ssl application is started before trying to connect to the database to run the migrations.

We need to commit this changes to the repository. DigitalOcean works by deploying a specific branch from our repository and building the Docker image with what it finds in that branch. If you plan to use Docker as your production environment then is fine to commit this to the main branch and setup DigitalOcean to use the main branch for deployment. For this example, I will use a different branch to commit these changes and configure DigitalOcean to use that branch. I'll use a branch named digital-ocean-deployment

git checkout -b digital-ocean-deployment
git add .
git commit -m "Setup for deploying to DigitalOcean"
git push -u origin digital-ocean-deployment

Creating an App in DigitalOcean

Let's do the deployment to DigitalOcean.

Go to your DigitalOcean dashboard and create a new App:

Create New App

Select GitHub on the "Choose Source" screen:

Choose GitHub as a source

The first time you do this, GitHub will require you to authorize DigitalOcean to access your repositories. Follow the instructions and give access to the repository you just created

Authorize DigitalOcean

Authorization granted

You'll get back to the "Choose source" and you'll see the list of authorized repositories there

Image description

Select your repository and you'll see that the main branch is selected automatically

Repository selected

If you're using main that's fine. For this article, I'll change it to the digital-ocean-deployment branch:

Branch selected

Click Next to move to the "Configure your app" page:

Configure app

We need to configure the environment variables that DigitalOcean will provide to our Docker container in runtime. We can omit the PORT environment variable as DigitalOcean automatically provides it. We have to create the POOL_SIZE, DATABASE_URL, and SECRET_KEY_BASE. Let's start with the last one.

Click on the Edit link in the "Environment Variables" section. Then, in a terminal execute this command:

mix phx.gen.secret

Add a variable named SECRET_KEY_BASE and put the value you got from the command in it. Check the "Encrypt" option.

SECRET_KEY_BASE environment variable

Now create a POOL_SIZE with value 2 and a DATABASE_URL with value ${db.DATABASE_URL}. These two don't need to be encrypted.

Environment variables

We need to create a database. Click on the "Add a Database" button:

Add a Database

Accept the default values for the database and now you're ready to create the App in DigitalOcean.

One thing to note: the DATABASE_URL environment variable is referring to this database we just created. It will be replaced with the correct value to connect to this database when the application starts.

Click next to proceed to the "Name your web service" page:

Name your web service

Accept the defaults and click Next. You're now in the "Finalize and launch" page:

Finalize and launch

Click the "Launch Basic App" and you'll see your app being created.

App creation

DigitalOcean will now:

  1. Access your repository
  2. Clone it,
  3. Detect the Dockerfile in it
  4. Build a Docker image from it
  5. Push it to their own Container Registry
  6. Provision the database
  7. Set up environment variables
  8. Provision a server to run the Docker container
  9. Run the application

You can check the progress by inspecting the logs:

Realtime logs

At the end you'll have your app deployed:

Deployment successful

Run the migrations

You can access the elixir container console and run the migrations. Go to the "Console" tab and there evaluate the following command:

bin/saturn eval "Saturn.Release.migrate"

You'll see something like this:

Run DB migrations

Finally, visit the application by clicking the "Live App" button:

Live App

You'll see your app running in DigitalOcean's infrastructure:

Image description

We are done!

Source code

The source code for the saturn project is open source under the MIT license. Use the digital-ocean-deployment branch.


I'm Miguel Cobá. I write about Elixir, Elm, Software Development, and eBook writing.

Photo by Kris-Mikael Krister on Unsplash