<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ivan Iraci</title>
    <description>The latest articles on DEV Community by Ivan Iraci (@ilsanto).</description>
    <link>https://dev.to/ilsanto</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F192623%2F161f6f93-1d97-4d55-880e-08367be661db.jpeg</url>
      <title>DEV Community: Ivan Iraci</title>
      <link>https://dev.to/ilsanto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ilsanto"/>
    <language>en</language>
    <item>
      <title>Deploy a Phoenix app with Docker stack</title>
      <dc:creator>Ivan Iraci</dc:creator>
      <pubDate>Wed, 10 Jul 2019 15:33:40 +0000</pubDate>
      <link>https://dev.to/ilsanto/deploy-a-phoenix-app-with-docker-stack-1j9c</link>
      <guid>https://dev.to/ilsanto/deploy-a-phoenix-app-with-docker-stack-1j9c</guid>
      <description>&lt;h1&gt;
  
  
  The mandatory introductions
&lt;/h1&gt;

&lt;p&gt;Hi everybody, this is my very first post on DEV and I want it to be as short as possible, so that anyone could be able to go straight to the point, if deploying a Phoenix app on docker is the problem at hand.&lt;/p&gt;

&lt;p&gt;We will take advantage of &lt;a href="http://blog.plataformatec.com.br/2019/04/whats-new-in-elixir-apr-19/"&gt;the new "mix release" feature released with Elixir 1.9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I will assume your app needs a Postgres DB. If your architecture is more complex than this (Redis, Mongo, whatever) the deployment strategy for any other piece of software included in your architecture is beyond the scope of this article.&lt;/p&gt;

&lt;p&gt;Ok, let's go!&lt;/p&gt;

&lt;h1&gt;
  
  
  Releasing locally...
&lt;/h1&gt;

&lt;h2&gt;
  
  
  ... first without docker
&lt;/h2&gt;

&lt;p&gt;In the following examples, our app's name is Demo (so replace any occurrence of "demo" with your app's real name).&lt;/p&gt;

&lt;p&gt;First of all we have to make sure our app will "mix release" locally with a production environment setup.&lt;/p&gt;

&lt;p&gt;Run the following command in you console, at the root of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;mix release.init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then create a &lt;strong&gt;releases.exs&lt;/strong&gt; file inside your project's &lt;strong&gt;/config&lt;/strong&gt; dir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Config&lt;/span&gt;

&lt;span class="n"&gt;secret_key_base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SECRET_KEY_BASE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"APP_PORT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app_hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"APP_HOSTNAME"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DB_USER"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DB_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DB_HOST"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;DemoWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;http:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:inet6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;port:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_port&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="ss"&gt;secret_key_base:&lt;/span&gt; &lt;span class="n"&gt;secret_key_base&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;app_port:&lt;/span&gt; &lt;span class="n"&gt;app_port&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;app_hostname:&lt;/span&gt; &lt;span class="n"&gt;app_hostname&lt;/span&gt;

&lt;span class="c1"&gt;# Configure your database&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Demo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;username:&lt;/span&gt; &lt;span class="n"&gt;db_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;password:&lt;/span&gt; &lt;span class="n"&gt;db_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;database:&lt;/span&gt; &lt;span class="s2"&gt;"demo_prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;hostname:&lt;/span&gt; &lt;span class="n"&gt;db_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;pool_size:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are going to keep all of our production "secrets" in an &lt;strong&gt;.env&lt;/strong&gt; file in the root of our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;APP_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4000
&lt;span class="nv"&gt;APP_HOSTNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost
&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres
&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pass
&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost
&lt;span class="nv"&gt;SECRET_KEY_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Y0uRvErYsecr3TANDL0ngStr1ng
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;APP_HOSTNAME&lt;/strong&gt; will be &lt;em&gt;localhost&lt;/em&gt; for testing your app locally but later it will need to be set to your real domain name (e.g.: &lt;em&gt;myverycoolapp.com&lt;/em&gt;), as you see in the comments of &lt;strong&gt;/config/prod.exs&lt;/strong&gt; which needs to be edited as follows, in order to get &lt;em&gt;host&lt;/em&gt; and &lt;em&gt;port&lt;/em&gt; from our &lt;strong&gt;.env&lt;/strong&gt; file. Make sure to uncomment the last line and to remove the &lt;strong&gt;&lt;em&gt;"import_config "prod.secret.exs"&lt;/em&gt;&lt;/strong&gt; from the file (since our "secrets" are in &lt;strong&gt;.env&lt;/strong&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;

&lt;span class="c1"&gt;# Don't forget to configure the url host to something meaningful,&lt;/span&gt;
&lt;span class="c1"&gt;# Phoenix uses this information when generating URLs.&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;DemoWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;load_from_system_env:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;url:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;host:&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:app_hostname&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;port:&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:app_port&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="ss"&gt;cache_static_manifest:&lt;/span&gt; &lt;span class="s2"&gt;"priv/static/cache_manifest.json"&lt;/span&gt;

&lt;span class="c1"&gt;# Do not print debug messages in production&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;level:&lt;/span&gt; &lt;span class="ss"&gt;:info&lt;/span&gt;

&lt;span class="c1"&gt;# Which server to start per endpoint:&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;DemoWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;server:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Remember to edit &lt;strong&gt;init/2&lt;/strong&gt; in &lt;strong&gt;/lib/demo_web/endpoint.ex&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="nv"&gt;@doc&lt;/span&gt; &lt;span class="sd"&gt;"""
  Callback invoked for dynamically configuring the endpoint.

  It receives the endpoint configuration and checks if
  configuration should be loaded from the system environment.
  """&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:load_from_system_env&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:demo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:app_port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"expected the PORT environment variable to be set"&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Keyword&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:inet6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;port:&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To be able to manage our migrations in the released app, we need to create a &lt;strong&gt;/lib/demo/release.ex&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Demo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Release&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@app&lt;/span&gt; &lt;span class="ss"&gt;:demo&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;migrate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;repos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_repo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:up&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;all:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_repo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to:&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;repos&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ecto_repos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ok, we are ready to try to release our app locally.&lt;/p&gt;

&lt;p&gt;All you have to do is execute the following commands in your console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;mix phx.digest
&lt;span class="nv"&gt;MIX_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod mix release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If nothing went wrong, you will be welcomed with the following instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* assembling demo-0.1.0 on MIX_ENV=prod                                                                                                               
* using config/releases.exs to configure the release at runtime                                                                                       
* skipping elixir.bat for windows (bin/elixir.bat not found in the Elixir installation)                                                               
* skipping iex.bat for windows (bin/iex.bat not found in the Elixir installation)                                                                     

Release created at _build/prod/rel/demo!

    # To start your system
    _build/prod/rel/demo/bin/demo start

Once the release is running:

    # To connect to it remotely
    _build/prod/rel/demo/bin/demo remote

    # To stop it gracefully (you may also send SIGINT/SIGTERM)
    _build/prod/rel/demo/bin/demo stop

To list all commands:

    _build/prod/rel/demo/bin/demo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;But before we can start and try our released app, we need to migrate our database, typing the following command in our console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .env
_build/prod/rel/demo/bin/demo &lt;span class="nb"&gt;eval &lt;/span&gt;Demo.Release.migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, you can start your app as suggested above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;_build/prod/rel/demo/bin/demo start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Is your app working as intended? I hope so. If yes we can move on.&lt;/p&gt;

&lt;h2&gt;
  
  
  ... and then with docker
&lt;/h2&gt;

&lt;p&gt;We want our app to be as light as it can be, so we are going to use two docker images based on &lt;em&gt;elixir:alpine&lt;/em&gt; and, of course, &lt;em&gt;alpine&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We are going to have a multistage Dockerfile. In the first stage we are going to build our release, in the second one we are going to deploy our released app and a postgres client (that we will use to know if the database is ready to accept connections and to run our migrations).&lt;/p&gt;

&lt;p&gt;This is our &lt;strong&gt;Dockerfile&lt;/strong&gt; and I suggest you to read it very carefully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---- Build Stage ----&lt;/span&gt;
FROM elixir:alpine AS app_builder

&lt;span class="c"&gt;# Set environment variables for building the application&lt;/span&gt;
ENV &lt;span class="nv"&gt;MIX_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;TEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;LANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C.UTF-8

RUN apk add &lt;span class="nt"&gt;--update&lt;/span&gt; git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/cache/apk/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Install hex and rebar&lt;/span&gt;
RUN mix local.hex &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    mix local.rebar &lt;span class="nt"&gt;--force&lt;/span&gt;

&lt;span class="c"&gt;# Create the application build directory&lt;/span&gt;
RUN &lt;span class="nb"&gt;mkdir&lt;/span&gt; /app
WORKDIR /app

&lt;span class="c"&gt;# Copy over all the necessary application files and directories&lt;/span&gt;
COPY config ./config
COPY lib ./lib
COPY priv ./priv
COPY mix.exs &lt;span class="nb"&gt;.&lt;/span&gt;
COPY mix.lock &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Fetch the application dependencies and build the application&lt;/span&gt;
RUN mix deps.get
RUN mix deps.compile
RUN mix phx.digest
RUN mix release

&lt;span class="c"&gt;# ---- Application Stage ----&lt;/span&gt;
FROM alpine AS app

ENV &lt;span class="nv"&gt;LANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C.UTF-8

&lt;span class="c"&gt;# Install openssl&lt;/span&gt;
RUN apk add &lt;span class="nt"&gt;--update&lt;/span&gt; openssl ncurses-libs postgresql-client &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/cache/apk/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Copy over the build artifact from the previous step and create a non root user&lt;/span&gt;
RUN adduser &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /home/app app
WORKDIR /home/app
COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;app_builder /app/_build &lt;span class="nb"&gt;.&lt;/span&gt;
RUN &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; app: ./prod
USER app

COPY entrypoint.sh &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Run the Phoenix app&lt;/span&gt;
CMD &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"./entrypoint.sh"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create an &lt;strong&gt;entrypoint.sh&lt;/strong&gt; (and make it executable) at the root of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# Docker entrypoint script.&lt;/span&gt;

&lt;span class="c"&gt;# Wait until Postgres is ready&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; pg_isready &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nv"&gt;$DB_HOST&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 5432 &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="nv"&gt;$DB_USER&lt;/span&gt;
&lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - waiting for database to start"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep &lt;/span&gt;2
&lt;span class="k"&gt;done&lt;/span&gt;

./prod/rel/demo/bin/demo &lt;span class="nb"&gt;eval &lt;/span&gt;Demo.Release.migrate

./prod/rel/demo/bin/demo start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can build our image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; demo-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Since we need to have a postgres instance running, here is a &lt;strong&gt;docker-compose.yml&lt;/strong&gt; that will take care of both our app and a database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;version: &lt;span class="s1"&gt;'3.1'&lt;/span&gt;

services:

  database:
    image: postgres
    restart: always
    environment:
      POSTGRES_USER: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      POSTGRES_PASSWORD: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      POSTGRES_DB: demo_prod

  web:
    image: demo-app
    restart: always
    ports:
      - &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
    environment:
      APP_PORT: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      DB_USER: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      DB_PASSWORD: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      DB_HOST: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      SECRET_KEY_BASE: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SECRET_KEY_BASE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
    depends_on:
      - database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now you need to edit your &lt;strong&gt;.env&lt;/strong&gt; and change &lt;em&gt;at least&lt;/em&gt; the &lt;em&gt;DB_HOST&lt;/em&gt; (you can leave the db credentials unchanged, the database container will take care of creating the user and db for you):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;APP_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4000
&lt;span class="nv"&gt;APP_HOSTNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost
&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres
&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pass
&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;database
&lt;span class="nv"&gt;SECRET_KEY_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Y0uRvErYsecr3TANDL0ngStr1ng
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now you can start your containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If all is well, you can point your browser to &lt;em&gt;&lt;a href="http://localhost:4000"&gt;http://localhost:4000&lt;/a&gt;&lt;/em&gt; and your application will be there waiting for you.&lt;/p&gt;

&lt;p&gt;Now we are ready to write our &lt;strong&gt;docker-stack.yml&lt;/strong&gt;, so that we can deploy our app in production (in a DigitalOcean droplet, on AWS, on your own server, ...):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;version: &lt;span class="s1"&gt;'3.1'&lt;/span&gt;

services:

  database:
    image: postgres
    deploy:
      restart_policy:
        condition: on-failure
    environment:
      POSTGRES_USER: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      POSTGRES_PASSWORD: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      POSTGRES_DB: demo_prod
    networks:
      - backend

  web:
    image: foobar/demo-app:latest
    deploy:
      restart_policy:
        condition: on-failure
    ports:
      - &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
    environment:
      APP_PORT: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      APP_HOSTNAME: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_HOSTNAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      DB_USER: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      DB_PASSWORD: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      DB_HOST: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
      SECRET_KEY_BASE: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SECRET_KEY_BASE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
    depends_on:
      - database_migrator
    networks:
      - backend

networks:
  backend:

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Before deploying, we need to publish our application to the docker-hub of our choice (in the example above it is published as a fictional &lt;em&gt;foobar/demo-app:latest&lt;/em&gt;). Publishing an image to a docker-hub is out of the scope of this article, but if you are here I am positive that you already know how to do it...&lt;/p&gt;

&lt;p&gt;Now we have to create an &lt;strong&gt;.env-stack&lt;/strong&gt; for our deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;APP_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4000
&lt;span class="nv"&gt;APP_HOSTNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mycoolapp.com
&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres
&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pass
&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;database
&lt;span class="nv"&gt;SECRET_KEY_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Y0uRvErYsecr3TANDL0ngStr1ng
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, after you set up you swarm and connect to it (out of this scope, &lt;a href="https://docs.docker.com/machine/drivers/"&gt;see the docs on docs.docker.com&lt;/a&gt;), you can deploy your app as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .env-stack
docker stack deploy &lt;span class="nt"&gt;-c&lt;/span&gt; docker-stack.yml demo-app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Given that your swarm is configured to respond to the hostname mycoolapp.com, point your browser to &lt;em&gt;&lt;a href="http://mycoolapp.com:4000"&gt;http://mycoolapp.com:4000&lt;/a&gt;&lt;/em&gt; and that's all!&lt;/p&gt;

&lt;p&gt;Easy, uh? :-)&lt;/p&gt;

&lt;p&gt;I'm looking forward for all your constructive (but not only) criticisms and suggestions.&lt;/p&gt;

&lt;p&gt;Thanks and to the next.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>elixir</category>
      <category>phoenix</category>
    </item>
  </channel>
</rss>
