Install the CLI. See the install docs.
Log in:
runway login
Add an SSH key. See key setup, or use the shortcut:
runway local key setup
Let’s deploy a small HTTP service written in Elixir on Runway!
Create a new project for your Elixir app. In this example, the module is called Example, which you have to take into account with the code presented further down:
$ mix new --module Example my_app
Then go into the my_app directory:
$ cd my_app
Elixir uses mix to maintain dependencies, so let’s add what we need — most notably Cowboy (an HTTP-framework for Erlang/Elixir) into the mix.exs file:
defmodule Example.MixProject do
use Mix.Project
def project do
[
app: :example,
version: "0.1.0",
elixir: "~> 1.14",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
def application do
[
extra_applications: [:logger],
mod: {Example.Application, []}
]
end
defp deps do
[
{:plug_cowboy, "~> 2.6"} # Plug with Cowboy server
]
end
end
Then run mix deps.get to download all dependencies.
Define the overall application in lib/example/ directory. example is the module name we selected earlier when we created the project.
The application.ex is the overall entry point for our app and configures the web server through a PORT environment variable (with a default of 3000):
defmodule Example.Application do
use Application
def start(_type, _args) do
children = [
# Start the Plug Cowboy HTTP server
{Plug.Cowboy, scheme: :http, plug: Example.Router, options: [port: port()]}
]
opts = [strategy: :one_for_one, name: Example.Supervisor]
Supervisor.start_link(children, opts)
end
# Fetch the port from the environment or default to 3000
defp port do
try do
System.get_env("PORT") |> String.to_integer()
rescue
_ -> 3000
end
end
end
… followed by the route definitions and the handlers to send a response to a client. Create/open (lib/example/) router.ex and insert the following:
defmodule Example.Router do
use Plug.Router
use Plug.Logger
plug :match
plug :dispatch
get "/" do
send_resp(conn, 200, "Hello")
end
get "/hello-world" do
send_resp(conn, 200, "Hello World")
end
match _ do
send_resp(conn, 404, "Oops, not found!")
end
end
Finally, let’s configure the logger outside the lib/example directory and create config/config.exs with the following contents:
use Mix.Config
config :logger, :console,
format: "[$level] $message\n",
metadata: [:request_id],
level: :info
You created/edited the following files:
lib/example/application.exlib/example/router.exconfig/config.exsmix as well: mix run --no-halt will start the application on http://localhost:3000.Now that all is configured and setup, let’s continue with the build.
To build our application, we use the following multi-stage Dockerfile which we’ll add to the root of the repository:
FROM elixir:1.17-alpine AS build
WORKDIR /app
# Install build dependencies
RUN mix local.hex --force && mix local.rebar --force
# Copy the mix files and install dependencies
COPY mix.exs mix.lock ./
RUN mix deps.get
# Copy the source code and compile the application
COPY lib ./lib
RUN MIX_ENV=prod mix release
FROM elixir:1.17-alpine
# Set the user and group to nobody:nobody (in numeric)
USER 65534:65534
WORKDIR /app
ENV HOME=/app
# Configure the writable directories
ENV ERL_COOKIE_PATH=/data/.erlang.cookie
ENV ERL_CRASH_DUMP=/data/crash.dump
# Copy the compiled app
COPY --from=build /app/_build/prod/rel/example .
EXPOSE 3000
CMD ["/app/bin/example", "start"]
The build stage will setup the dependencies needed to compile your application and ultimately produce a release in /workspace/_build/prod/rel/example. The next (and final) stage copies the results and runs it.
/data.Create the app on Runway (this also initializes a git repository, if none exists yet), then commit your changes:
$ runway app create
$ git add -A
$ git commit -m "initial commit"
runway app deploy
Run runway app open to see it live, or check the Runway UI. Check runway app logs if something’s off.
Congrats, your Elixir app is now on Runway! 👊👊👊