Best deployment guide for a Phoenix API to AWS?

I’m looking to deploy a Phoenix API to AWS. I don’t need any fancy features, I just need to be able to select a specific node type (high compute), and ideally it could be easy to deploy nodes.

There are thousands of guides I’m finding, and the 3 i’ve tried were all broken.

Is there a really good guide (in terms of security, scalability, configuration, etc), which I can just fully commit to going through?


Have you considered I know they support deploying to AWS and have Elixir / Phoenix as a supported platform:

1 Like

Maybe for

1 Like

That guide is so broken that I just stopped following it.

This is the one I’m doing now:

It works great so far, the only part I’m confused on is where I define my environment variables so that REPLACE_OS_VARS from distillery can replace them. I’m assuming its not when i package the release, but after.

You can set REPLACE_OS_VARS in a pre configure hook.

As for the distillery guide, it wasn’t broken at all when I followed it a few months ago.

Hmm weird. I encountered like 3 or 4 places that I fixed easily enough but it didn’t give me confidence in the guide. ultimately I stopped though because I didnt want to depend on amazon blackbox services which Ive had experience with before. Granted, my current method deals with Google blackbox services but I trust those more for some reason.

If you look at that link you will see REPLACE_OS_VARS in the docker image, so I basically do this (all the environment variables at this point look like "${THIS_FORMAT}":

MIX_ENV=prod mix release --env=prod

At this point I can do:

REPLACE_OS_VARS=true MIX_ENV=prod DATABASE_NAME=... REDIS_URL=... _build/whatever/whatever foreground

and it works…


docker build --no-cache -t hello .

I can see in their docker image they have REPLACE_OS_VARS I’m just wondering where they fit in AFTER release but before docker build? Can you explain what you mean by pre-configure hook?

Also as a side question. One thing I found really strange was that I could mix release with your standard "${VAR_HERE}"… and then when i would RUN the release with REPLACE_OS_VARS=true ... foreground it would work! This part I dont understand, how does the app know AT RUN TIME to replace the variables? Is that some code distilierry injects into my app to make it know to do that at run time rather than at compile time?

Oh I see, its in the deploy scripts in _build/prod/rel/app/lib/


You can set REPLACE_OS_VARS just before running the app. It’s being deprecated in favor of config providers, though.

See for more.

Can you explain what you mean by pre-configure hook?

Damn well this looks super confusing. I’ll have to get docker running start to finish first then ill figure out how to do that.

How might I do that with this current strategy I’m doing, can you give me an examle of a single variable and where I put it so I don’t have to read all that (for now)

Let’s say you have something like

config :app, db_path: "${DB_PATH}"

in your config.exs somewhere.

To make it replaceable at runtime, you’d need to set REPLACE_OS_VARS before starting the release. One way to do it is in a pre configure hook.

Oh I’m talking about doing away with this method “${DB_PATH}” and REPLACE_OS_VARS and just doing it the proper way with config providers

im actually just reading now to see how to do it

You’d choose a config provider, copy it into the release, and that’s it, I think.

Somewhere in rel/config.exs

environment :prod do
  set config_providers: [
    {Mix.Releases.Config.Providers.Elixir, ["${RELEASE_ROOT_DIR}/etc/config.exs"]}
  set overlays: [
    {:copy, "rel/config/config.exs", "etc/config.exs"}

In rel/config/config.exs

use Mix.Config

config :app, db_path: System.get_env("DB_PATH")
1 Like

Dude thank you. Alright I’ll try doing that next

So I assume REPLACE_OS_VARS is still required in order to set release_root_dir?

You don’t need to set it yourself.

I’m not sure if distillery scripts use it for replacing ${RELEASE_ROOT_DIR} in its config.

The only use-case that I have for REPLACE_OS_VARS is setting the node name of the app to the ip address of the machine which is read at startup.

In rel/config.exs

environment :prod do
  # ...
  set(vm_args: "rel/vm.args")
  set(pre_configure_hooks: "rel/hooks/pre_configure")
  # ...

in rel/hooks/pre_configure/export_host_ipv4

#!/usr/bin/env bash

export HOST_IPV4
export REPLACE_OS_VARS=true

  # just as an example, might not work actually
  ifconfig \
    | sed -En 's/;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p' \
    | grep '^10\.' # only interested in private network

and in rel/vm.args

-name app@${HOST_IPV4}
% ... etc

Ok so this is a dumb question, but with config providers where do I actually provide the variables now? Like where and how do I ‘pass them in’?

Depends on the tools that you use. In kubernetes, I think env vars are set via yaml configs, in docker, ENV is used. In systemd Environment=, etc …

1 Like

For anyone else here i found this good article

1 Like

I would personally recommend going with Docker for deployments on VPS systems. This helps you not becoming dependent on an individual provider and makes it easier to create repeatable deployments.
Check out this guide on Getting Started with Elixir and Docker I’ve created and let me know if you find it helpful!

1 Like

That doesnt explain how to deploy it, it just explains using docker. But cool article