Build and Deploy Elixir Phoenix releases with CircleCI

Distillery, rsync, and ssh to the rescue

I’d really prefer to avoid using Docker or Vagrant if it’s not really necessary, and I am getting tired of paying for and running a build server for edeliver to
build releases of my applications.

I had thought about using a CI service to build releases in the past but never really gave it a shot, until recently I heard
someone talking about using their CI services to build releases and deploy.
This motivated me to try it for myself.

I decided to use CircleCi because I like the way their config works, their docs aren’t bad, and they have a
decent free tier (also their pricing isn’t bad if I need to grow).

1: Set up your project

2: Set up your server

You will need to have a user with the proper permissions for doing all the things(TM). I generally would use something like my_name and have my app under /home/my_name/apps/my_app.
Create the directories now. If you don’t use this structure, you will have to tweak the following config and scripts to match your own structure.

3: Set up the CI service

Connect your repository to the CI service and set up your environment variables.
I am using these in my config, although you could just hard-code these into your config.yml, doing it this way makes the script more copypasta-friendly.

In project settings under BUILD SETTINGS > Environment Variables:

APP_HOST=123.4.567.89
APP_NAME=my_app
APP_USER=my_name

Then you will also need to have an SSH key pair you will use for deploying your release to your production server.
If you don’t have a pair you’d like to use, you can generally just make it like:

This just sets up the basic elixir stuff, plus postgres for phoenix (if you need it for tests, or something).

I’m using a pre-built circle-ci image as my primary image here, where the base OS is Ubuntu, which is what I also use in production.
If you are not using Ubuntu on your production servers, you’ll probably want to use some different docker images. Check the different sections in the circle-ci docs
about containers to help you figure out how to choose different images if you are unsure.

IMPORTANT The line with sudo setcap, I have here in case I am not proxying with Nginx and am just
using Cowboy. I would need to set this in order to give beam permission to use the ports I want (like port 80).
I also have made this sudo command run without a sudo password by editing my sudoers file.
If you are using Nginx or something, and a non-protected port like 4000 for cowboy you can get rid of this line.

IMPORTANT 2 The line symlinking /var/www/html/.well-known is because that’s where I’ve set that directory
for LetsEncrypt. I also have it set in my MyAppWeb.Endpoint config for static files:

Conclusion

Now, you can push your code, run your tests, build your release, deploy and restart, all in one command and without needing a build server.

There could be more to do, like automatic versioning (I might explore that in a future update), or building upgrade releases, or exploring how to possibly improve build times with circleci caching and artifacts. However, this is a very good start for me now that I can get rid of my build server.

It may have been a dry topic, but I hope you enjoyed this as much as I did.