monoton

Deploying a Phoenix application II

While the previous post was all about the installation of Distillery we’ll now configure Phoenix to use a reverse proxy. Furthermore we will build and start our release for the first time! At the end of this post we’ll have a release of our application running in production.

Setup nginx

We want to use nginx as a reverse proxy. It should relay all requests to Phoenix which listens on localhost. You can follow this tutorial in order to setup/configure nginx as a proxy server. Also make sure to take a look at the official nginx considerations which explain some details regarding websockets.

Have a look at the nginx configuration of my last web application:

upstream phoenix_upstream {
  server 127.0.0.1:8888;
}

map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}

server {
  listen 80;
  server_name example.org;
  return 301 https://$server_name$request_uri;
}

server {
  listen  443 ssl;
  server_name example.org;
  root /var/www/example/;

  ssl_certificate /etc/letsencrypt/live/example.org/cert.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;


  location / {
    if (-f $document_root/maintenance.html) {
      return 503;
    }
    try_files $uri @proxy;
  }

  location @proxy {
    include proxy_params;
    proxy_redirect off;
    proxy_pass http://phoenix_upstream;

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
  }

  error_page 503 @maintenance;
  location @maintenance {
    rewrite ^(.*)$ /maintenance.html break;
  }
}

Maybe you noticed the @maintenance location: With this configuration nginx will display the maintenance.html if you place it inside your web root.

Configure Phoenix

Now we have to edit config/prod.exs once more. For now we are going to disable the dynamic configuration loading, set the URL of our web application (i.e. for the Phoenix path helpers) and set the host and port.

config :deploy_app, DeployAppWeb.Endpoint,
  load_from_system_env: false,
  http: [ip: {127, 0, 0, 1}, port: 8888],
  url: [host: "https://example.org", port: 80],
  ...

Build your release

Everything should be configured correctly now and we can continue with building our first release. Go into your assets directory and build all assets with Brunch.

$ cd assets/
$ node_modules/brunch/bin/brunch build -p
$ cd ..

Now digest your static files.

$ MIX_ENV=prod mix phoenix.digest

And build your own release for deployment!

$ MIX_ENV=prod mix release --env=prod

Get the release to your server

Distillery automatically generates a tar archive which contains all the relevant files. You can use scp to get it onto your server:

$ scp _build/prod/rel/deploy_app/releases/0.0.1/deploy_app.tar.gz user@server:~/

Now we’ll connect to our server over ssh and unpack it.

$ tar -xvf deploy_app.tar.gz

Test your application

Once we have unpacked our release we can start it by executing:

$ bin/deploy_app console

The shell will probably look familiar to you. If everything seems to run smoothly, we should be able to reach our app under the configured domain (https://example.org in my example).

Start your application

If the initial test went well we can now close the console and start our application the proper way. The start command will launch your application and move to the background.

$ bin/deploy_app start

Read the next post

Congratulations, you have now mastered a substantial part of Phoenix deployment!

Coming soon: In the next and final post of this series we’ll learn how to work with our running application and how to do hot code upgrading which is one of the first steps towards zero downtime. Additionally, we’ll create a systemd service for our release.