{"_id":"5777c96b5b2b430e00b982bd","__v":0,"project":"54348ec95b10711400c6c445","user":"5435e00ad7d8700800bbec51","version":{"_id":"5777c9635b2b430e00b982a5","__v":1,"project":"54348ec95b10711400c6c445","createdAt":"2016-07-02T14:02:11.084Z","releaseDate":"2016-07-02T14:02:11.084Z","categories":["5777c9635b2b430e00b982a6","5777c9635b2b430e00b982a7","5777c9635b2b430e00b982a8","5777c9635b2b430e00b982a9","5777c9635b2b430e00b982aa"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.2.0","version":"1.2.0"},"category":{"_id":"5777c9635b2b430e00b982a9","version":"5777c9635b2b430e00b982a5","project":"54348ec95b10711400c6c445","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-06-18T21:59:42.796Z","from_sync":false,"order":3,"slug":"deployment","title":"Deployment"},"parentDoc":null,"updates":["552581f158aa012100cefbf2","5617e0e326e3db230054fbf9","563a2a7df0c29b1700daaf97","5663596408aacc0d00e2d3f7","56791830239fac0d00c7df19","568ae758176a6c0d00a29eb8","56b0962014dfea0d0007cf90","56e7e6ca0628661900e55c0a","5714b3829eb7280e008d1dae","5717c8caa096be0e001af27b"],"next":{"pages":[],"description":""},"createdAt":"2015-03-05T18:26:39.770Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":2,"body":"### What We'll Need\n\nThere are just a few things we'll need before we continue.\n\n- a working application\n- a build environment - this can be our dev environment\n- a hosting environment - this can be our build environment for testing/experimentation\n\nWe need to be sure that the architectures for both our build and hosting environments are the same, e.g. 64-bit Linux -> 64-bit Linux. If the architectures don't match, our application might not run when deployed. Using a virtual machine that mirrors our hosting environment as our build environment is an easy way to avoid that problem.\n\n### Goals\n\nOur main goal for this guide is to generate a release, using the [Elixir Release Manager](https://github.com/bitwalker/exrm) (Exrm), and deploy it to our hosting environment. Once we have our application running, we will discuss steps needed to make it publicly visible.\n\n## Tasks\n\nLet's separate our release process into a few tasks so we can keep track of where we are.\n\n- Add exrm as a dependency\n- Generate a release\n- Test it\n- Deploy it\n- Make it public\n\n## Add exrm as a Dependency\n\nTo get started, we'll need to add `{:exrm, \"~> 1.0\"}` into the list of dependencies in our `mix.exs` file.\n\n```elixir\n  defp deps do\n    [{:phoenix, \"~> 1.2.0\"},\n     {:phoenix_ecto, \"~> 3.0\"},\n     {:postgrex, \">= 0.0.0\"},\n     {:phoenix_html, \"~> 2.3\"},\n     {:phoenix_live_reload, \"~> 1.0\", only: :dev},\n     {:cowboy, \"~> 1.0\"},\n     {:exrm, \"~> 1.0\"}]\n  end\n```\n\nWith that taken care of, a simple `mix do deps.get, compile` will pull down exrm and its dependencies, along with the rest of our application's dependencies. It also ensures that everything compiles properly. If all goes well, exrm's mix tasks will be available.\n\n```console\n$ mix help\n. . .\nmix release           # Build a release for the current mix application.\nmix release.clean     # Clean up any release-related files.\nmix release.plugins   # View information about active release plugins\n. . .\n```\n\n### Configure Our Applications\n\nNow we need to update our `mix.exs` file to have all production dependencies listed in the `applications` list in the `application/0` function.\n\n```elixir\n  def application do\n    [mod: {HelloPhoenix, []},\n     applications: [:phoenix, :cowboy, :logger, :postgrex, :gettext,\n                    :phoenix_ecto, :phoenix_html]]\n  end\n```\n\nDoing this helps us overcome one of [exrm's common issues](https://hexdocs.pm/exrm/common-issues.html) by helping exrm know about all our dependencies so that it can properly bundle them into our release. Without this, our application will probably alert us about missing modules or a failure to start a child application when we go to run our release.\n\nEven if we list all of our dependencies, our application may still fail. Typically, this happens because one of our dependencies does not properly list its own dependencies. A quick fix for this is to include the missing dependency or dependencies in our list of applications. If this happens to you, and you feel like helping the community, you can create an issue or a pull request to that project's repo.\n\nWithin `config/prod.exs` we need to make the following changes.   We must configure our Endpoint to act as a server `server: true`.  We must set the `version: Mix.Project.config[:version]` to ensure that new versions cause the Endpoint assets cache to be reloaded.\n\n```elixir\n# Configures the endpoint\nconfig :hello_phoenix, HelloPhoenix.Endpoint,\nhttp: [port: 8888],\nurl: [host: \"example.com\"],\nroot: \".\",\ncache_static_manifest: \"priv/static/manifest.json\",\nserver: true,\nversion: Mix.Project.config[:version]\n```\n\nWhen we run `mix phoenix.server` to start our application, the `server` parameter is automatically set to true. When we're creating a release, however, we need to configure this manually. If we get through this release guide, and we aren't seeing any pages coming from our server, this is a likely culprit.\n\nWhen generating a release and performing a hot upgrade, the static assets being served will not be the newest version without setting the root to `.` and setting the endpoint version.  If `root` or `version` are not set then we will be forced to perform a restart so that the correct static assets are served, effectively changing the hot upgrade to a rolling restart.\n\nIf we take a quick look at our `config/prod.exs` again, we'll see that our port is set to `8888`.\n\n```elixir\n. . .\nconfig :hello_phoenix, HelloPhoenix.Endpoint,\nhttp: [port: 8888],\n. . .\n```\n\nAlternately, we can set the port from an environment variable on our system.\n\n```elixir\n. . .\nconfig :hello_phoenix, HelloPhoenix.Endpoint,\nhttp: [port: {:system, \"PORT\"}],\n. . .\n```\n\nWe also need to uncomment the following line to instruct Phoenix to start the server for all endpoints:\n\n```elixir\n. . .\n# ## Using releases\n#\n# If you are doing OTP releases, you need to instruct Phoenix\n# to start the server for all endpoints:\n#\n    config :phoenix, :serve_endpoints, true\n. . .\n```\n\nIf we don't currently have such an environment variable, we need to set it now, otherwise our release will not build properly.\n\nThere's one last thing to do before we create our release. We need to pre-compile our static assets using the `phoenix.digest` task. We will be generating a production release, so we need to run this task with `MIX_ENV=prod`.\n\n```console\n$ MIX_ENV=prod mix phoenix.digest\n==> ranch (compile)\n. . .\nCheck your digested files at 'priv/static'.\n```\n\n### Generating the Release\n\nNow that we've configured our application, let's build our production release by running `MIX_ENV=prod mix compile` and then `MIX_ENV=prod mix release` at the root of our application.\n\n```console\n$ MIX_ENV=prod mix release\n\n. . .\n\nGenerated hello_phoenix app\nConsolidated Ecto.Queryable\nConsolidated Phoenix.Param\nConsolidated Phoenix.HTML.FormData\nConsolidated Phoenix.HTML.Safe\nConsolidated Plug.Exception\nConsolidated Poison.Decoder\nConsolidated Poison.Encoder\nConsolidated Access\nConsolidated Collectable\nConsolidated Enumerable\nConsolidated Inspect\nConsolidated List.Chars\nConsolidated Range.Iterator\nConsolidated String.Chars\nConsolidated protocols written to _build/prod/consolidated\n==> Building release with MIX_ENV=prod.\n==> Generating relx configuration...\n==> Generating sys.config...\n==> Generating boot script...\n==> Performing protocol consolidation...\n==> Conform: Loading schema...\n==> Conform: No schema found, conform will not be packaged in this release!\n==> Generating release...\n==> Generating nodetool...\n==> Packaging release...\n==> The release for hello_phoenix-0.0.1 is ready!\n```\n\nThere are a couple of interesting things to note here.\n\n- There are a number of lines which begin with \"Consolidated\". These are protocols which have been consolidated for faster function dispatch and better performance.\n\n- We see our application's version number - `0.0.1`. This value comes from the application's `mix.exs` file. It is mapped to the `:version` key inside the `project/0` function.\n\n- Exrm has created a `rel` directory at the root of our application where it has written everything related to this release.\n\nExrm uses a set of default configuration options when building our release that will work for most applications. If we need more advanced configuration options, we can checkout [exrm's configuration section](https://hexdocs.pm/exrm/release-configuration.html) for more detailed information.\n\nIf we make a mistake, or if something doesn't go quite right, we can run `mix release.clean`, which will delete the release for the current application version number. If we add the `--implode` flag, exrm will remove _all_ releases for all versions of our application. These will be permanently removed unless they are under version control. Obviously, this is a destructive operation, and exrm will prompt us to make sure we want to continue.\n\n#### Contents of a Release\n\nExrm has created our release, and put it somewhere in the `rel` directory, but where exactly did all the pieces end up?\n\nEverything related to our releases is in the `rel/hello_phoenix` directory. Let's see what's in it.\n\n```console\n$ ls -la rel/hello_phoenix/\ntotal 27216\ndrwxr-xr-x   7 lance  staff       238 May 13 18:47 .\ndrwxr-xr-x   3 lance  staff       102 May 13 18:47 ..\ndrwxr-xr-x   6 lance  staff       204 May 13 18:47 bin\ndrwxr-xr-x   8 lance  staff       272 May 13 18:47 erts-6.4\n-rw-r--r--   1 lance  staff  13933031 May 13 18:47 hello_phoenix-0.0.1.tar.gz\ndrwxr-xr-x  26 lance  staff       884 May 13 18:47 lib\ndrwxr-xr-x   5 lance  staff       170 May 13 18:47 releases\n```\n\nThe `bin` directory contains the generated executables for running our application. The `bin/hello_phoenix` executable is what we will use to issue commands to our application.\n\n```console\n$ ls -la rel/hello_phoenix/bin\ntotal 80\ndrwxr-xr-x  6 lance  staff    204 May 13 18:47 .\ndrwxr-xr-x  7 lance  staff    238 May 13 18:47 ..\n-rwxr-xr-x  1 lance  staff  13868 May 13 18:47 hello_phoenix\n-rw-r--r--  1 lance  staff   4400 May 13 18:47 install_upgrade.escript\n-rwxr-xr-x  1 lance  staff   5373 May 13 18:47 nodetool\n-rw-r--r--  1 lance  staff   5283 Apr 18  2014 start_clean.boot\n```\n\nThe `erts-6.4` directory contains all necessary files for the Erlang runtime system, pulled from our build environment.\n\n```console\n$ ls -la rel/hello_phoenix/erts-6.3/\ntotal 8\ndrwxr-xr-x   8 lance  staff  272 May 13 18:47 .\ndrwxr-xr-x   7 lance  staff  238 May 13 18:47 ..\ndrwxr-xr-x  24 lance  staff  816 May 13 18:47 bin\ndrwxr-xr-x  12 lance  staff  408 May 13 18:47 include\ndrwxr-xr-x   5 lance  staff  170 May 13 18:47 lib\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 src\n```\n\nThe `lib` directory contains the compiled BEAM files for our application and all of our dependencies. This is where all of our work goes.\n\n```console\n$ ls -la rel/hello_phoenix/lib/\notal 0\ndrwxr-xr-x  26 lance  staff  884 May 13 18:47 .\ndrwxr-xr-x   7 lance  staff  238 May 13 18:47 ..\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 compiler-5.0\ndrwxr-xr-x  16 lance  staff  544 May 13 18:47 consolidated\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 cowboy-1.0.0\ndrwxr-xr-x   4 lance  staff  136 May 13 18:47 cowlib-1.0.1\ndrwxr-xr-x   4 lance  staff  136 May 13 18:47 crypto-3.3\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 decimal-1.1.0\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 ecto-0.11.2\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 eex-1.0.4\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 elixir-1.0.4\ndrwxr-xr-x   4 lance  staff  136 May 13 18:47 hello_phoenix-0.0.1\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 iex-1.0.4\ndrwxr-xr-x   4 lance  staff  136 May 13 18:47 kernel-3.0\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 logger-1.0.4\ndrwxr-xr-x   4 lance  staff  136 May 13 18:47 phoenix-0.13.1\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 phoenix_ecto-0.4.0\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 phoenix_html-1.0.1\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 plug-0.12.2\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 poison-1.4.0\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 poolboy-1.5.1\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 postgrex-0.8.1\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 ranch-1.0.0\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 sasl-2.4\ndrwxr-xr-x   4 lance  staff  136 May 13 18:47 stdlib-2.0\ndrwxr-xr-x   3 lance  staff  102 May 13 18:47 syntax_tools-1.6.14\n```\n\nThe `releases` directory is the home for our releases - any release-dependent configurations and scripts that Exrm finds necessary for running our application. If we have multiple versions of our application, and if we have created releases for them, we will have multiple releases in the `releases` directory.\n\n```console\n$ ls -la rel/hello_phoenix/releases/\ntotal 16\ndrwxr-xr-x  5 lance  staff   170 May 13 18:47 .\ndrwxr-xr-x  7 lance  staff   238 May 13 18:47 ..\ndrwxr-xr-x  8 lance  staff   272 May 13 18:47 0.0.1\n-rw-r--r--  1 lance  staff  1241 May 13 18:47 RELEASES\n-rw-r--r--  1 lance  staff     9 May 13 18:47 start_erl.data\n```\n\nThe `hello_phoenix-0.0.1.tar.gz` tarball in `rel/hello_phoenix/releases/0.0.1` is our release in archive form, ready to be shipped off to our hosting environment.\n\n### Testing Our Release\n\nBefore deploying our release, we should make sure that it runs. To do that, we will issue the `console` command to our executable, essentially running our application via `iex`.\n\nNote: Since we are building a production release - we set our mix environment to \"prod\" when we created it - we should exercise a little extra caution.\n\nExternal dependencies will use their production configuration values. Applications will try to communicate with production databases, production Amazon S3 buckets, production message queues, and anything else which has a production configuration.\n\nSome of these might be unreachable from the build environment, which will cause errors. Some might interact with important production data. Please be careful.\n\nWith a newly-generated application, though, we should be fine. :)\n\nWith all that in mind, let's start up a console.\n\n```console\n$ rel/hello_phoenix/bin/hello_phoenix console\nExec: /Users/lance/work/hello_phoenix/rel/hello_phoenix/erts-6.0/bin/erlexec -boot /Users/lance/work/hello_phoenix/rel/hello_phoenix/releases/0.0.1/hello_phoenix -boot_var ERTS_LIB_DIR /Users/lance/work/hello_phoenix/rel/hello_phoenix/erts-6.0/../lib -env ERL_LIBS /Users/lance/work/hello_phoenix/rel/hello_phoenix/lib -config /Users/lance/work/hello_phoenix/rel/hello_phoenix/releases/0.0.1/sys.config -pa /Users/lance/work/hello_phoenix/rel/hello_phoenix/lib/consolidated -args_file /Users/lance/work/hello_phoenix/rel/hello_phoenix/releases/0.0.1/vm.args -user Elixir.IEx.CLI -extra --no-halt +iex -- console\nRoot: /Users/lance/work/hello_phoenix/rel/hello_phoenix\n/Users/lance/work/hello_phoenix/rel/hello_phoenix\nErlang/OTP 17 [erts-6.0] [source-07b8f44] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]\n\n[debug] Running HelloPhoenix.Endpoint with Cowboy on port 4000 (http)\nInteractive Elixir (1.0.2) - press Ctrl+C to exit (type h() ENTER for help)\niex(hello_phoenix:::at:::127.0.0.1)1>\n```\n\nThis is the point where our application will crash if it fails to start a child application. If all goes well, however, we should end up at an `iex` prompt. We should also see our app running at [http://localhost:8888/](http://localhost:8888/).\n\nLet's hit `ctrl-c` twice to get out of iex so that we can explore a couple of different ways to interact with our release.\n\nOne thing we can do is start the release without a console session. Let's try running the `start` command.\n\n```console\n$ rel/hello_phoenix/bin/hello_phoenix start\n```\nAnd we see . . . nothing except that our prompt comes right back. This is ok!\n\nWe can check to make sure that the release really is ok by pinging it.\n\n```console\n$ rel/hello_phoenix/bin/hello_phoenix ping\npong\n```\nGreat, it's responding.\n\nNow let's try connecting a console to the running release with the `remote_console` command.\n\n```console\n$ rel/hello_phoenix/bin/hello_phoenix remote_console\nErlang/OTP 17 [erts-6.0] [source-07b8f44] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]\n\nInteractive Elixir (1.0.2) - press Ctrl+C to exit (type h() ENTER for help)\niex(hello_phoenix@127.0.0.1)1>\n```\n\nThat worked. At this point, we can run any commands we might normally run in a console session.\n\nOk, let's get out of this session by hitting `ctrl-c` twice again. What happens if we ping the release now?\n\n```console\n$ rel/hello_phoenix/bin/hello_phoenix ping\npong\n```\n\nIt's still alive and responding. This is a feature! If the server were to go down every time we stopped a remote console, that would be a problem. Note that this situation is different from when we ran the `console` command above. The `remote_console` command is not intended to start a release locally, but rather to connect to one which has already started.\n\nSo how _do_ we stop the server? There are two ways.\n\nOne way is to simply issue the `stop` command.\n\n```console\n$ rel/hello_phoenix/bin/hello_phoenix stop\nok\n```\n\nThat looks promising. What happens if we ping the server again?\n\n```console\n$ rel/hello_phoenix/bin/hello_phoenix ping\nNode 'hello_phoenix@127.0.0.1' not responding to pings.\n```\nSuccess.\n\nOk, let's re-start our release and establish a remote console to try the other way of stopping it.\n\n```console\n$ rel/hello_phoenix/bin/hello_phoenix start\n$ rel/hello_phoenix/bin/hello_phoenix remote_console\nErlang/OTP 17 [erts-6.0] [source-07b8f44] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]\n\nInteractive Elixir (1.0.2) - press Ctrl+C to exit (type h() ENTER for help)\niex(hello_phoenix@127.0.0.1)1>\n```\n\nGreat. Now at the prompt, let's issue this command `:init.stop`.\n\n```console\niex(hello_phoenix@127.0.0.1)1> :init.stop\n:ok\n```\n\nThen let's hit `ctrl-c` twice again to exit our iex session and ping the server to see if it responds.\n\n```console\n$ rel/hello_phoenix/bin/hello_phoenix ping\nNode 'hello_phoenix@127.0.0.1' not responding to pings.\n```\n\nAs we expected, the server is now down.\n\nCongratulations! Now that we've interacted a bit with our release locally, we're ready to deploy our application!\n\n## Deploying Our Release\n\nThere are many ways for us to get our tarballed release to our hosting environment. In our example, we'll use SCP to upload to a remote server.\n\n```console\n$ scp -i ~/.ssh/id_rsa.pub rel/hello_phoenix-0.0.1.tar.gz ubuntu@hostname.com:/home/ubuntu\nhello_phoenix-0.0.1.tar.gz                100%   18MB  80.0KB/s   03:48\n```\n\nLet's SSH into that environment to set our application up.\n\n```console\n$ ssh -i ~/.ssh/id_rsa.pub ubuntu@hostname.com\n$ sudo mkdir -p /app\n$ sudo chown ubuntu:ubuntu /app\n$ cd /app\n$ tar xfz /home/ubuntu/hello_phoenix-0.0.1.tar.gz\n```\n\n## Making Our Application Public\n\nWe're getting close.\n\n### Setting Up Our Init System\n\nFirst step in exposing our application to the world is ensuring that our application will start running in case of a system restart - expected or unexpected. To do this, we will need to create an init script for our hosting environment's init system, be it `systemd`, `upstart`, or whatever.\n\nLet's use `upstart` as an example. We'll edit our init script with `sudo vi /etc/init/hello_phoenix.conf` (this is on Ubuntu Linux).\n\n```text\ndescription \"hello_phoenix\"\n\n## Uncomment the following two lines to run the\n## application as www-data:www-data\n#setuid www-data\n#setgid www-data\n\nstart on runlevel [2345]\nstop on runlevel [016]\n\nexpect stop\nrespawn\n\nenv MIX_ENV=prod\nexport MIX_ENV\n\n## Uncomment the following two lines if we configured\n## our port with an environment variable.\n#env PORT=8888\n#export PORT\n\n## Add app HOME directory.\nenv HOME=/app\nexport HOME\n\n\npre-start exec /bin/sh /app/bin/hello_phoenix start\n\npost-stop exec /bin/sh /app/bin/hello_phoenix stop\n```\n\nHere, we've told `upstart` a few basic things about how we want it to handle our application. If you need to know how to do something in particular, take a look at the [`upstart` cookbook](http://upstart.ubuntu.com/cookbook/) for loads of information on it. We'll kick off the first start of our application with `sudo start hello_phoenix`.\n\nOne key point to notice is that we're instructing `upstart` to run our release's `bin/hello_phoenix start` command, which boostraps our application and runs it as a daemon.\n\n### Setting Up Our Web Server\n\nIn a lot of cases, we're going to have more than one application running in our hosting environment, all of which might need to be accessible on port 80. Since only one application can listen on a single port at a time, we need to use something to proxy our application. Typically, Apache (with `mod_proxy` enabled) or nginx is used for this, and we'll be setting up nginx in this case.\n\nLet's create our config file for our application. By default, everything in `/etc/nginx/sites-enabled` is included into the main `/etc/nginx/nginx.conf` file that is used to configure nginx's runtime environment. Standard practice is to create our file in `/etc/nginx/sites-available` and make a symbolic link to it in `/etc/nginx/sites-enabled`.\n\nNote: These points hold true for Apache as well, but the steps to accomplish them are slightly different.\n\n```console\n$ sudo touch /etc/nginx/sites-available/hello_phoenix\n$ sudo ln -s /etc/nginx/sites-available/hello_phoenix /etc/nginx/sites-enabled\n$ sudo vi /etc/nginx/sites-available/hello_phoenix\n```\n\nThese are the contents of our `/etc/nginx/sites-available/hello_phoenix` file.\n\n```nginx\nupstream hello_phoenix {\n    server 127.0.0.1:8888;\n}\n# The following map statement is required\n# if you plan to support channels. See https://www.nginx.com/blog/websocket-nginx/\nmap $http_upgrade $connection_upgrade {\n    default upgrade;\n    '' close;\n}\nserver{\n    listen 80;\n    server_name .hostname.com;\n\n    location / {\n        try_files $uri @proxy;\n    }\n\n    location @proxy {\n        include proxy_params;\n        proxy_redirect off;\n        proxy_pass http://hello_phoenix;\n        # The following two headers need to be set in order\n        # to keep the websocket connection open. Otherwise you'll see\n        # HTTP 400's being returned from websocket connections.\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection $connection_upgrade;\n    }\n}\n```\n\nLike our `upstart` script, this nginx config is basic. Look to the [nginx wiki](https://www.nginx.com/resources/wiki/) for steps to configure any more involved features. Restart nginx with `sudo service nginx restart` to load our new config.\n\nAt this point, we should be able to see our application if we visit `http://hostname.com/` if everything has been successful up to this point.","excerpt":"","slug":"advanced-deployment","type":"basic","title":"Exrm Releases"}
### What We'll Need There are just a few things we'll need before we continue. - a working application - a build environment - this can be our dev environment - a hosting environment - this can be our build environment for testing/experimentation We need to be sure that the architectures for both our build and hosting environments are the same, e.g. 64-bit Linux -> 64-bit Linux. If the architectures don't match, our application might not run when deployed. Using a virtual machine that mirrors our hosting environment as our build environment is an easy way to avoid that problem. ### Goals Our main goal for this guide is to generate a release, using the [Elixir Release Manager](https://github.com/bitwalker/exrm) (Exrm), and deploy it to our hosting environment. Once we have our application running, we will discuss steps needed to make it publicly visible. ## Tasks Let's separate our release process into a few tasks so we can keep track of where we are. - Add exrm as a dependency - Generate a release - Test it - Deploy it - Make it public ## Add exrm as a Dependency To get started, we'll need to add `{:exrm, "~> 1.0"}` into the list of dependencies in our `mix.exs` file. ```elixir defp deps do [{:phoenix, "~> 1.2.0"}, {:phoenix_ecto, "~> 3.0"}, {:postgrex, ">= 0.0.0"}, {:phoenix_html, "~> 2.3"}, {:phoenix_live_reload, "~> 1.0", only: :dev}, {:cowboy, "~> 1.0"}, {:exrm, "~> 1.0"}] end ``` With that taken care of, a simple `mix do deps.get, compile` will pull down exrm and its dependencies, along with the rest of our application's dependencies. It also ensures that everything compiles properly. If all goes well, exrm's mix tasks will be available. ```console $ mix help . . . mix release # Build a release for the current mix application. mix release.clean # Clean up any release-related files. mix release.plugins # View information about active release plugins . . . ``` ### Configure Our Applications Now we need to update our `mix.exs` file to have all production dependencies listed in the `applications` list in the `application/0` function. ```elixir def application do [mod: {HelloPhoenix, []}, applications: [:phoenix, :cowboy, :logger, :postgrex, :gettext, :phoenix_ecto, :phoenix_html]] end ``` Doing this helps us overcome one of [exrm's common issues](https://hexdocs.pm/exrm/common-issues.html) by helping exrm know about all our dependencies so that it can properly bundle them into our release. Without this, our application will probably alert us about missing modules or a failure to start a child application when we go to run our release. Even if we list all of our dependencies, our application may still fail. Typically, this happens because one of our dependencies does not properly list its own dependencies. A quick fix for this is to include the missing dependency or dependencies in our list of applications. If this happens to you, and you feel like helping the community, you can create an issue or a pull request to that project's repo. Within `config/prod.exs` we need to make the following changes. We must configure our Endpoint to act as a server `server: true`. We must set the `version: Mix.Project.config[:version]` to ensure that new versions cause the Endpoint assets cache to be reloaded. ```elixir # Configures the endpoint config :hello_phoenix, HelloPhoenix.Endpoint, http: [port: 8888], url: [host: "example.com"], root: ".", cache_static_manifest: "priv/static/manifest.json", server: true, version: Mix.Project.config[:version] ``` When we run `mix phoenix.server` to start our application, the `server` parameter is automatically set to true. When we're creating a release, however, we need to configure this manually. If we get through this release guide, and we aren't seeing any pages coming from our server, this is a likely culprit. When generating a release and performing a hot upgrade, the static assets being served will not be the newest version without setting the root to `.` and setting the endpoint version. If `root` or `version` are not set then we will be forced to perform a restart so that the correct static assets are served, effectively changing the hot upgrade to a rolling restart. If we take a quick look at our `config/prod.exs` again, we'll see that our port is set to `8888`. ```elixir . . . config :hello_phoenix, HelloPhoenix.Endpoint, http: [port: 8888], . . . ``` Alternately, we can set the port from an environment variable on our system. ```elixir . . . config :hello_phoenix, HelloPhoenix.Endpoint, http: [port: {:system, "PORT"}], . . . ``` We also need to uncomment the following line to instruct Phoenix to start the server for all endpoints: ```elixir . . . # ## Using releases # # If you are doing OTP releases, you need to instruct Phoenix # to start the server for all endpoints: # config :phoenix, :serve_endpoints, true . . . ``` If we don't currently have such an environment variable, we need to set it now, otherwise our release will not build properly. There's one last thing to do before we create our release. We need to pre-compile our static assets using the `phoenix.digest` task. We will be generating a production release, so we need to run this task with `MIX_ENV=prod`. ```console $ MIX_ENV=prod mix phoenix.digest ==> ranch (compile) . . . Check your digested files at 'priv/static'. ``` ### Generating the Release Now that we've configured our application, let's build our production release by running `MIX_ENV=prod mix compile` and then `MIX_ENV=prod mix release` at the root of our application. ```console $ MIX_ENV=prod mix release . . . Generated hello_phoenix app Consolidated Ecto.Queryable Consolidated Phoenix.Param Consolidated Phoenix.HTML.FormData Consolidated Phoenix.HTML.Safe Consolidated Plug.Exception Consolidated Poison.Decoder Consolidated Poison.Encoder Consolidated Access Consolidated Collectable Consolidated Enumerable Consolidated Inspect Consolidated List.Chars Consolidated Range.Iterator Consolidated String.Chars Consolidated protocols written to _build/prod/consolidated ==> Building release with MIX_ENV=prod. ==> Generating relx configuration... ==> Generating sys.config... ==> Generating boot script... ==> Performing protocol consolidation... ==> Conform: Loading schema... ==> Conform: No schema found, conform will not be packaged in this release! ==> Generating release... ==> Generating nodetool... ==> Packaging release... ==> The release for hello_phoenix-0.0.1 is ready! ``` There are a couple of interesting things to note here. - There are a number of lines which begin with "Consolidated". These are protocols which have been consolidated for faster function dispatch and better performance. - We see our application's version number - `0.0.1`. This value comes from the application's `mix.exs` file. It is mapped to the `:version` key inside the `project/0` function. - Exrm has created a `rel` directory at the root of our application where it has written everything related to this release. Exrm uses a set of default configuration options when building our release that will work for most applications. If we need more advanced configuration options, we can checkout [exrm's configuration section](https://hexdocs.pm/exrm/release-configuration.html) for more detailed information. If we make a mistake, or if something doesn't go quite right, we can run `mix release.clean`, which will delete the release for the current application version number. If we add the `--implode` flag, exrm will remove _all_ releases for all versions of our application. These will be permanently removed unless they are under version control. Obviously, this is a destructive operation, and exrm will prompt us to make sure we want to continue. #### Contents of a Release Exrm has created our release, and put it somewhere in the `rel` directory, but where exactly did all the pieces end up? Everything related to our releases is in the `rel/hello_phoenix` directory. Let's see what's in it. ```console $ ls -la rel/hello_phoenix/ total 27216 drwxr-xr-x 7 lance staff 238 May 13 18:47 . drwxr-xr-x 3 lance staff 102 May 13 18:47 .. drwxr-xr-x 6 lance staff 204 May 13 18:47 bin drwxr-xr-x 8 lance staff 272 May 13 18:47 erts-6.4 -rw-r--r-- 1 lance staff 13933031 May 13 18:47 hello_phoenix-0.0.1.tar.gz drwxr-xr-x 26 lance staff 884 May 13 18:47 lib drwxr-xr-x 5 lance staff 170 May 13 18:47 releases ``` The `bin` directory contains the generated executables for running our application. The `bin/hello_phoenix` executable is what we will use to issue commands to our application. ```console $ ls -la rel/hello_phoenix/bin total 80 drwxr-xr-x 6 lance staff 204 May 13 18:47 . drwxr-xr-x 7 lance staff 238 May 13 18:47 .. -rwxr-xr-x 1 lance staff 13868 May 13 18:47 hello_phoenix -rw-r--r-- 1 lance staff 4400 May 13 18:47 install_upgrade.escript -rwxr-xr-x 1 lance staff 5373 May 13 18:47 nodetool -rw-r--r-- 1 lance staff 5283 Apr 18 2014 start_clean.boot ``` The `erts-6.4` directory contains all necessary files for the Erlang runtime system, pulled from our build environment. ```console $ ls -la rel/hello_phoenix/erts-6.3/ total 8 drwxr-xr-x 8 lance staff 272 May 13 18:47 . drwxr-xr-x 7 lance staff 238 May 13 18:47 .. drwxr-xr-x 24 lance staff 816 May 13 18:47 bin drwxr-xr-x 12 lance staff 408 May 13 18:47 include drwxr-xr-x 5 lance staff 170 May 13 18:47 lib drwxr-xr-x 3 lance staff 102 May 13 18:47 src ``` The `lib` directory contains the compiled BEAM files for our application and all of our dependencies. This is where all of our work goes. ```console $ ls -la rel/hello_phoenix/lib/ otal 0 drwxr-xr-x 26 lance staff 884 May 13 18:47 . drwxr-xr-x 7 lance staff 238 May 13 18:47 .. drwxr-xr-x 3 lance staff 102 May 13 18:47 compiler-5.0 drwxr-xr-x 16 lance staff 544 May 13 18:47 consolidated drwxr-xr-x 3 lance staff 102 May 13 18:47 cowboy-1.0.0 drwxr-xr-x 4 lance staff 136 May 13 18:47 cowlib-1.0.1 drwxr-xr-x 4 lance staff 136 May 13 18:47 crypto-3.3 drwxr-xr-x 3 lance staff 102 May 13 18:47 decimal-1.1.0 drwxr-xr-x 3 lance staff 102 May 13 18:47 ecto-0.11.2 drwxr-xr-x 3 lance staff 102 May 13 18:47 eex-1.0.4 drwxr-xr-x 3 lance staff 102 May 13 18:47 elixir-1.0.4 drwxr-xr-x 4 lance staff 136 May 13 18:47 hello_phoenix-0.0.1 drwxr-xr-x 3 lance staff 102 May 13 18:47 iex-1.0.4 drwxr-xr-x 4 lance staff 136 May 13 18:47 kernel-3.0 drwxr-xr-x 3 lance staff 102 May 13 18:47 logger-1.0.4 drwxr-xr-x 4 lance staff 136 May 13 18:47 phoenix-0.13.1 drwxr-xr-x 3 lance staff 102 May 13 18:47 phoenix_ecto-0.4.0 drwxr-xr-x 3 lance staff 102 May 13 18:47 phoenix_html-1.0.1 drwxr-xr-x 3 lance staff 102 May 13 18:47 plug-0.12.2 drwxr-xr-x 3 lance staff 102 May 13 18:47 poison-1.4.0 drwxr-xr-x 3 lance staff 102 May 13 18:47 poolboy-1.5.1 drwxr-xr-x 3 lance staff 102 May 13 18:47 postgrex-0.8.1 drwxr-xr-x 3 lance staff 102 May 13 18:47 ranch-1.0.0 drwxr-xr-x 3 lance staff 102 May 13 18:47 sasl-2.4 drwxr-xr-x 4 lance staff 136 May 13 18:47 stdlib-2.0 drwxr-xr-x 3 lance staff 102 May 13 18:47 syntax_tools-1.6.14 ``` The `releases` directory is the home for our releases - any release-dependent configurations and scripts that Exrm finds necessary for running our application. If we have multiple versions of our application, and if we have created releases for them, we will have multiple releases in the `releases` directory. ```console $ ls -la rel/hello_phoenix/releases/ total 16 drwxr-xr-x 5 lance staff 170 May 13 18:47 . drwxr-xr-x 7 lance staff 238 May 13 18:47 .. drwxr-xr-x 8 lance staff 272 May 13 18:47 0.0.1 -rw-r--r-- 1 lance staff 1241 May 13 18:47 RELEASES -rw-r--r-- 1 lance staff 9 May 13 18:47 start_erl.data ``` The `hello_phoenix-0.0.1.tar.gz` tarball in `rel/hello_phoenix/releases/0.0.1` is our release in archive form, ready to be shipped off to our hosting environment. ### Testing Our Release Before deploying our release, we should make sure that it runs. To do that, we will issue the `console` command to our executable, essentially running our application via `iex`. Note: Since we are building a production release - we set our mix environment to "prod" when we created it - we should exercise a little extra caution. External dependencies will use their production configuration values. Applications will try to communicate with production databases, production Amazon S3 buckets, production message queues, and anything else which has a production configuration. Some of these might be unreachable from the build environment, which will cause errors. Some might interact with important production data. Please be careful. With a newly-generated application, though, we should be fine. :) With all that in mind, let's start up a console. ```console $ rel/hello_phoenix/bin/hello_phoenix console Exec: /Users/lance/work/hello_phoenix/rel/hello_phoenix/erts-6.0/bin/erlexec -boot /Users/lance/work/hello_phoenix/rel/hello_phoenix/releases/0.0.1/hello_phoenix -boot_var ERTS_LIB_DIR /Users/lance/work/hello_phoenix/rel/hello_phoenix/erts-6.0/../lib -env ERL_LIBS /Users/lance/work/hello_phoenix/rel/hello_phoenix/lib -config /Users/lance/work/hello_phoenix/rel/hello_phoenix/releases/0.0.1/sys.config -pa /Users/lance/work/hello_phoenix/rel/hello_phoenix/lib/consolidated -args_file /Users/lance/work/hello_phoenix/rel/hello_phoenix/releases/0.0.1/vm.args -user Elixir.IEx.CLI -extra --no-halt +iex -- console Root: /Users/lance/work/hello_phoenix/rel/hello_phoenix /Users/lance/work/hello_phoenix/rel/hello_phoenix Erlang/OTP 17 [erts-6.0] [source-07b8f44] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] [debug] Running HelloPhoenix.Endpoint with Cowboy on port 4000 (http) Interactive Elixir (1.0.2) - press Ctrl+C to exit (type h() ENTER for help) iex(hello_phoenix@127.0.0.1)1> ``` This is the point where our application will crash if it fails to start a child application. If all goes well, however, we should end up at an `iex` prompt. We should also see our app running at [http://localhost:8888/](http://localhost:8888/). Let's hit `ctrl-c` twice to get out of iex so that we can explore a couple of different ways to interact with our release. One thing we can do is start the release without a console session. Let's try running the `start` command. ```console $ rel/hello_phoenix/bin/hello_phoenix start ``` And we see . . . nothing except that our prompt comes right back. This is ok! We can check to make sure that the release really is ok by pinging it. ```console $ rel/hello_phoenix/bin/hello_phoenix ping pong ``` Great, it's responding. Now let's try connecting a console to the running release with the `remote_console` command. ```console $ rel/hello_phoenix/bin/hello_phoenix remote_console Erlang/OTP 17 [erts-6.0] [source-07b8f44] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Interactive Elixir (1.0.2) - press Ctrl+C to exit (type h() ENTER for help) iex(hello_phoenix@127.0.0.1)1> ``` That worked. At this point, we can run any commands we might normally run in a console session. Ok, let's get out of this session by hitting `ctrl-c` twice again. What happens if we ping the release now? ```console $ rel/hello_phoenix/bin/hello_phoenix ping pong ``` It's still alive and responding. This is a feature! If the server were to go down every time we stopped a remote console, that would be a problem. Note that this situation is different from when we ran the `console` command above. The `remote_console` command is not intended to start a release locally, but rather to connect to one which has already started. So how _do_ we stop the server? There are two ways. One way is to simply issue the `stop` command. ```console $ rel/hello_phoenix/bin/hello_phoenix stop ok ``` That looks promising. What happens if we ping the server again? ```console $ rel/hello_phoenix/bin/hello_phoenix ping Node 'hello_phoenix@127.0.0.1' not responding to pings. ``` Success. Ok, let's re-start our release and establish a remote console to try the other way of stopping it. ```console $ rel/hello_phoenix/bin/hello_phoenix start $ rel/hello_phoenix/bin/hello_phoenix remote_console Erlang/OTP 17 [erts-6.0] [source-07b8f44] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Interactive Elixir (1.0.2) - press Ctrl+C to exit (type h() ENTER for help) iex(hello_phoenix@127.0.0.1)1> ``` Great. Now at the prompt, let's issue this command `:init.stop`. ```console iex(hello_phoenix@127.0.0.1)1> :init.stop :ok ``` Then let's hit `ctrl-c` twice again to exit our iex session and ping the server to see if it responds. ```console $ rel/hello_phoenix/bin/hello_phoenix ping Node 'hello_phoenix@127.0.0.1' not responding to pings. ``` As we expected, the server is now down. Congratulations! Now that we've interacted a bit with our release locally, we're ready to deploy our application! ## Deploying Our Release There are many ways for us to get our tarballed release to our hosting environment. In our example, we'll use SCP to upload to a remote server. ```console $ scp -i ~/.ssh/id_rsa.pub rel/hello_phoenix-0.0.1.tar.gz ubuntu@hostname.com:/home/ubuntu hello_phoenix-0.0.1.tar.gz 100% 18MB 80.0KB/s 03:48 ``` Let's SSH into that environment to set our application up. ```console $ ssh -i ~/.ssh/id_rsa.pub ubuntu@hostname.com $ sudo mkdir -p /app $ sudo chown ubuntu:ubuntu /app $ cd /app $ tar xfz /home/ubuntu/hello_phoenix-0.0.1.tar.gz ``` ## Making Our Application Public We're getting close. ### Setting Up Our Init System First step in exposing our application to the world is ensuring that our application will start running in case of a system restart - expected or unexpected. To do this, we will need to create an init script for our hosting environment's init system, be it `systemd`, `upstart`, or whatever. Let's use `upstart` as an example. We'll edit our init script with `sudo vi /etc/init/hello_phoenix.conf` (this is on Ubuntu Linux). ```text description "hello_phoenix" ## Uncomment the following two lines to run the ## application as www-data:www-data #setuid www-data #setgid www-data start on runlevel [2345] stop on runlevel [016] expect stop respawn env MIX_ENV=prod export MIX_ENV ## Uncomment the following two lines if we configured ## our port with an environment variable. #env PORT=8888 #export PORT ## Add app HOME directory. env HOME=/app export HOME pre-start exec /bin/sh /app/bin/hello_phoenix start post-stop exec /bin/sh /app/bin/hello_phoenix stop ``` Here, we've told `upstart` a few basic things about how we want it to handle our application. If you need to know how to do something in particular, take a look at the [`upstart` cookbook](http://upstart.ubuntu.com/cookbook/) for loads of information on it. We'll kick off the first start of our application with `sudo start hello_phoenix`. One key point to notice is that we're instructing `upstart` to run our release's `bin/hello_phoenix start` command, which boostraps our application and runs it as a daemon. ### Setting Up Our Web Server In a lot of cases, we're going to have more than one application running in our hosting environment, all of which might need to be accessible on port 80. Since only one application can listen on a single port at a time, we need to use something to proxy our application. Typically, Apache (with `mod_proxy` enabled) or nginx is used for this, and we'll be setting up nginx in this case. Let's create our config file for our application. By default, everything in `/etc/nginx/sites-enabled` is included into the main `/etc/nginx/nginx.conf` file that is used to configure nginx's runtime environment. Standard practice is to create our file in `/etc/nginx/sites-available` and make a symbolic link to it in `/etc/nginx/sites-enabled`. Note: These points hold true for Apache as well, but the steps to accomplish them are slightly different. ```console $ sudo touch /etc/nginx/sites-available/hello_phoenix $ sudo ln -s /etc/nginx/sites-available/hello_phoenix /etc/nginx/sites-enabled $ sudo vi /etc/nginx/sites-available/hello_phoenix ``` These are the contents of our `/etc/nginx/sites-available/hello_phoenix` file. ```nginx upstream hello_phoenix { server 127.0.0.1:8888; } # The following map statement is required # if you plan to support channels. See https://www.nginx.com/blog/websocket-nginx/ map $http_upgrade $connection_upgrade { default upgrade; '' close; } server{ listen 80; server_name .hostname.com; location / { try_files $uri @proxy; } location @proxy { include proxy_params; proxy_redirect off; proxy_pass http://hello_phoenix; # The following two headers need to be set in order # to keep the websocket connection open. Otherwise you'll see # HTTP 400's being returned from websocket connections. proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } } ``` Like our `upstart` script, this nginx config is basic. Look to the [nginx wiki](https://www.nginx.com/resources/wiki/) for steps to configure any more involved features. Restart nginx with `sudo service nginx restart` to load our new config. At this point, we should be able to see our application if we visit `http://hostname.com/` if everything has been successful up to this point.