A nifty way to setup an Elixir development environment is to use Docker instead of using asdf
or installing the Elixir toolchain locally.
Docker has the option to mount a directory into the container which we can exploit to mount whatever folder we are currently in by using the unix command pwd
(print working directory).
We can also instruct Docker to expose a port and remove the container once it is stopped. Putting these things together we can get a Elixir environment entirely contained in Docker. The base of the command would be this in bash:
docker run --mount type=bind,source=$(pwd),target=/app -p 4000:4000 --rm elixir:latest
From this point we can extend it to do whatever we want with Elixir, such as running mix
commands.
Although we have to create a Docker image that has these tools contained. Here is an example of a Docker image that you can build with docker build -t elixir-env .
in the directory of the Dockerfile:
# Extend from the official Elixir image
FROM elixir:latest
RUN mix local.hex --force \
&& mix archive.install --force hex phx_new \
&& apt-get update \
&& curl -sL https://deb.nodesource.com/setup_lts.x | bash \
&& apt-get install -y apt-utils \
&& apt-get install -y nodejs \
&& apt-get install -y build-essential \
&& apt-get install -y inotify-tools \
&& mix local.rebar --force
ENV APP_HOME /app
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
EXPOSE 4000
CMD ["mix", "phx.server"]
After creating the image and tagging it with elixir-env
we can create a Phoenix application by running that container in bash:
docker run --mount type=bind,source=$(pwd),target=/app -p 4000:4000 --rm elixir-env:latest mix phx.server
But where this becomes really interesting is when you alias it to a function in your shell.
Doing that will give you the base CLI tool and make it just as accessible as having it installed locally.
To alias the function you simply do:
alias mix="docker run --mount type=bind,source=$(pwd),target=/app -p 4000:4000 --rm elixir:latest mix"
From here we can simply use mix
to run any further invocations of that container. Thus we can replace it with the local installation and be oblivious to it running through Docker.
This technique is quite powerful for testing out environments or if you can't get a specific program to run locally on your machine because of conflicting dependencies. Creating a program running in Docker saves you from dealing with global dependencies and allows you the flexibility of a clean installation.
Top comments (4)
Hi,
I tested it on Docker for Mac 4.3.1 and M1 Mac according to this post.
I created a phoenix project and did
mix deps.get
as follows afterdocker build -t elixir-env .
:Then, I launched a
pax-server
as follows:Though I viewed
http://localhost:4000
by a web browser on the host, I can't connect thephx.server
.What wrong?
Hi Susumu ( @zacky1972 ). Thanks for your question.
It seems like if you want to generate a new phoenix project you are asked if you want to install dependencies. Docker exits abruptly on that question without adding
-it
to therun
command. Can I get you to try your above example again, but instead do:This should allow you to install the dependencies interactively.
But the actual issue is a change that happened to the
config/dev.exs
file, only allowing access from the same computer. Just change thehttp
in that file to allow access from0.0.0.0
like so:Then launch the phoenix server again with:
Btw, I liked your talks at Elixir Conf.
I did it according to your answer and it was done! Thank you so much.
And also, thank you for watching and listening to my talk!
Hi Jamie,
thanks for your tutorial. Unfortunately i just stumble on the second step. After building/tagging the image i got this error msg when i try to run a the command:
docker run --mount type=bind,source=$(pwd),target=/app -p 4000:4000 --rm elixir-env:latest mix phx.server
** (Mix) The task "phx.server" could not be found
Note no mix.exs was found in the current directory
BTW, I'm on macos big sour 11.6.8