DEV Community

Cover image for Deploying Elixir LiveView for free on Render using Docker
Luan Gomes
Luan Gomes

Posted on

Deploying Elixir LiveView for free on Render using Docker

Deploying an Elixir application has been complicated some time ago, but now we have so many tools that facilitate our job that is very simple to set up a production environment for free.

Render is a platform as a service that offers a host for our server, DDoS protection, auto-deploy on git, and many more features that we can use without putting a credit card! They support Docker as well, so we are going to use it.

The objective here is to create a simple LiveView application, change it and deploy it on Render using a Docker image.

So let's start!

road

Creating the app and running on docker locally

Our application will be called Render Deploy, it has no functionality, it is just to demonstrate the deployment.

To create a LiveView app, run the command below:

mix phx.new render_deploy --live --no-ecto

  • - - live: This is the indication that the new project is a LiveView app.
  • - - no-ecto: Indicates that we don't want ecto, so it will not have a Repo.

Now, on the config/dev.exs, line 12 change 127, 0, 0, 1 to 0, 0, 0, 0 to allow access from docker.

dev.exs config

We must create the ./Dockerfile on the directory root to run locally:

FROM hexpm/elixir:1.13.0-erlang-23.3.4.10-alpine-3.14.3 AS base

WORKDIR /render_deploy

RUN mix do local.hex --force, local.rebar --force

RUN apk add npm inotify-tools
Enter fullscreen mode Exit fullscreen mode
  • This is a docker multistage build, it keeps the file more readable and creates a smaller image, you can read more here.
  • FROM hexpm/elixir:1.13.0-erlang-23.3.4.10-alpine-3.14.3 AS base: Here we are pulling the elixir image based on alpine, which is a smaller image.
  • WORKDIR /render_deploy: This will be our work directory, inside the container.
  • RUN mix do local.hex --force, local.rebar --force: Installs precompiled hex and rebar.
  • RUN apk add npm inotify-tools: Live reloading tool

We could run docker build and docker run, but I prefer using docker-compose to manage the containers, so let's create a new ./docker-compose.yml on the directory root.

version: "3.8"

services:
  app:
    build:
      context: .
      target: base
    container_name: render_deploy_web
    command: mix phx.server
    restart: unless-stopped
    ports:
      - 4000:4000
    volumes:
      - .:/render_deploy
Enter fullscreen mode Exit fullscreen mode
  • With multistage build we can set a specific target when creating the container, in this case, is target: base.

After creating the files we can run these commands to install dependencies, create the container and run the server locally.

docker-compose run --rm app mix deps.get

docker-compose up

If we can access localhost:4000 we will see this page:

phoenix home screen

Let's change it to make it unique.

lib/render_deploy_web/templates/page/index.html.heex line 2 change

<h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
to
<h1><%= gettext "Welcome to %{name}!", name: "Render deploy" %></h1>
Enter fullscreen mode Exit fullscreen mode

changed home screen

Adjusting the code before deploy

We are going to use mix releases, which is a way to package our application into an executable binary and use it to deploy the application.

For this, we must add the following code to the end of ./Dockerfile file:

# -----------------
# BUILD
# -----------------
FROM base AS build

RUN apk add curl bash git

ARG MIX_ENV=prod
ENV MIX_ENV=$MIX_ENV
COPY . ./

# install application
RUN mix do deps.get, compile

# -----------------
# RELEASE
# -----------------
FROM build AS release

# digests and compresses static files
RUN mix assets.deploy

# generate release executable
RUN mix release

# -----------------
# PRODUCTION
# -----------------
FROM alpine:3.14.3

WORKDIR /render_deploy

ARG MIX_ENV=prod

# install dependencies
RUN apk add ncurses-libs curl

COPY --from=release /render_deploy/_build/$MIX_ENV/rel/render_deploy ./

# start application
CMD ["bin/render_deploy", "start"]
Enter fullscreen mode Exit fullscreen mode
  • Important things about the code:
  • ARG MIX_ENV=prod: We set our mix env to prod.
  • RUN mix assets.deploy: minify esbuild and compresses static files.
  • COPY --from=release /render_deploy/_build/$MIX_ENV/rel/render_deploy ./: Copy the previous build stage to the current stage.
  • CMD ["bin/render_deploy", "start"]: It will start the application.

Now, before deploying we must adjust our config/runtime.exs so Render will put the production URL:

import Config

if System.get_env("PHX_SERVER") && System.get_env("RELEASE_NAME") do
  config :render_deploy, RenderDeployWeb.Endpoint, server: true
end

if config_env() == :prod do
  secret_key_base =
    System.get_env("SECRET_KEY_BASE") ||
      raise """
      environment variable SECRET_KEY_BASE is missing.
      You can generate one by calling: mix phx.gen.secret
      """

  port = String.to_integer(System.get_env("PORT") || "4000")

  config :render_deploy, RenderDeployWeb.Endpoint,
    url: [host: System.get_env("RENDER_EXTERNAL_HOSTNAME") || "localhost", port: 80],
    http: [
      ip: {0, 0, 0, 0, 0, 0, 0, 0},
      port: port
    ],
    secret_key_base: secret_key_base

  config :render_deploy, RenderDeployWeb.Endpoint, server: true
end
Enter fullscreen mode Exit fullscreen mode

Preparing the deploy

Ok, we have our development setup done with docker and we are ready to deploy in production with render.com

First of all, we need to create a repository with the code on Github.

Now let's access render.com and create the project, if you don't have an account, create it then click on New, Web Service.

render create new account

Select your repository:

Selecting repository

Choose a name, select Docker as the environment runtime and the free plan.

Now we must set the secret key, to do this, run the following command to generate a secret:

docker-compose run --rm app mix phx.gen.secret

choosing name and env

After this, scroll down the page and click on "Advanced", then put the SECRET_KEY_BASE with the secret generated before.

Now we just click on "Create Web Service" and wait for Render to deploy our application to production.

putting secret key base env

clicking on create new service

When the deployment is completed you will see the "Live" label, now just click on the URL and check our new LiveView application running on the cloud 🎉

deployment completed

application on cloud url

Render has some limitations on the free plan, the apps are automatically spun down after 15 minutes of inactivity and a new request after this time can take 30 seconds to be up again, also the free plan allows for 750 hours of running time per month across all free web services in your account, you can see all limitations here.

Even with these constraints, it is a nice platform to have our services and to practice the production deployment.

You can check the source code here: https://github.com/Lgdev07/render_deploy and the application that we've just built here: https://render-example-71rp.onrender.com/

I appreciate everyone who has read through here, if you guys have anything to add, please leave a comment.

Discussion (0)