What is Render?

Render is a Platform as a Service (PaaS) cloud hosting solution that can host web applications, static websites and databases (currently PostgreSQL). Render provides free TLS certificates and supports deploying directly from Git.

What you need for this guide

The only thing you need to follow this guide is a working Phoenix 1.7 application. Throughout this guide, <APP_NAME> is the initial application name and <MODULE_NAME> is the base module name for your application.

Finding your application name (<APP_NAME>) and module name (<MODULE_NAME>)

NOTE: You can get the application name and module name from config/config.exs as shown below:

config/config.exs
import Config
config :deploy_to_render, ecto_repos: [DeployToRender.Repo]

Create a new Phoenix application if you need a simple application to deploy

Terminal window
mix phx.new deploy_to_render
Fetch and install dependencies? [Yn] Y
cd deploy_to_render
mix ecto.create
mix phx.server

If you need more information or want some troubleshooting information, refer to the Up and Running Guide

Create a build script

When preparing an application for deployment, there are three main steps:

  • Handling of your application secrets
  • Compiling your application assets
  • Starting your server in production

In order to assemble a release on Render, we need a build script. I’ve used the build script provided by Render and simplified it a bit.

build.sh
#!/usr/bin/env bash
# exit on error
set -o errexit
# Initial setup
mix deps.get --only prod
MIX_ENV=prod mix compile
# Compile assets
MIX_ENV=prod mix assets.deploy
# Build the release and overwrite the existing release directory
MIX_ENV=prod mix release --overwrite

Make sure that the script is executable before checking it into Git by running chmod a+x build.sh on the command line.

You no longer need to follow any of the instructions in the Configure Mix Releases section or the Update your App for Render section in the Render documentation. The config/runtime.exs file generated by mix phx.gen now uses configuration options via environment variables that make deployment on Render much simpler than in previous Phoenix releases.

Create a web Service on Render

Create a new Web Service on Render and connect it to your GitHub or GitLab repository. The values that you need to get right during creation are. Please make sure to replace <APP_NAME> with your own application name. I’ve described how to find your application name earlier in this guide:

SettingValue
RuntimeElixir
Build Command./build.sh
Start Command_build/prod/rel/<APP_NAME>/bin/<APP_NAME> start

NOTE: You’ll need a paid instance type for your web service if you want to run a Pre-Deploy Command which is needed to run Ecto migrations on deployment.

Set environment variables

The last section when you are creating a Web Service are the Environment Variables. You need to set these two Environment Variables:

Name of VariableValue
PHX_SERVERtrue
PORT4000
SECRET_KEY_BASERun mix phx.gen.secret from the command line and use the generated value here
DATABASE_URLCreate a PostgreSQL database on Render and copy the value of the Internal Database URL once the database has been created. You can follow this guide with the free instance type.

Click the “Create Web Service” button.

Specify an Elixir and Erlang version

It’s good practice to set the Elixir version that your application requires, usually the version that you’ve been developing on. Without this, Render will use a default version for Elixir (and Erlang), which can cause unexpected problems in the future.

Set the Elixir version your application expects in your Environment variables:

Name of VariableValue
ELIXIR_VERSIONRun ‘elixir -v’ on your command line to find out the Elixir version you’re developing on
ERLANG_VERSIONDon’t set this value; Render automatically downloads an Erlang runtime that’s compatible with your chosen Elixir version

You can get a list of all supported Elixir versions on Render here.

Checkpoint 1: Deploy Phoenix application

You should now be able to visit the URL provided by Render to view your deployed Phoenix application.

Run Ecto migrations on every deploy

NOTE: A paid tier web service instance type is needed from this point onwards.

Create a module that allows you to run database tasks like migration or rollback when the application is running in production without Mix installed:

lib/deploy_to_render/release.ex
defmodule DeployToRender.Release do
@app :deploy_to_render
def migrate do
load_app()
for repo <- repos() do
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
end
end
def rollback(repo, version) do
load_app()
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
end
defp repos do
Application.fetch_env!(@app, :ecto_repos)
end
defp load_app do
Application.load(@app)
end
end

Go to your web service’s settings on Render and set a Pre-Deploy command:

SettingValue
Pre-Deploy Command_build/prod/rel/<APP_NAME>/bin/<APP_NAME> eval "<MODULE_NAME>.Release.migrate"

Now commit the module to your code repository to trigger a new deploy. From now on, pending Ecto migrations will run before every deploy.

Checkpoint 2: Run Ecto migrations before every deployment

Your application should now connect to a PostgreSQL database and run migrations before every new deployment.

Add a custom domain

Follow Render’s guide to adding a custom domain in order to add your custom domain to point to the newly created web service on Render. Once the DNS records have been verified and an SSL certificate has been generated, you should be able to visit your application on your custom domain.

Fix LiveViews

There is one last issue which shows up only when you use Phoenix LiveViews AND use a custom domain on Render.

Let’s build a simple LiveView from scratch to see the issue.

Add a live Route

/lib/deploy_to_render_web/router.ex
  scope "/", DeployToRenderWeb do
    pipe_through :browser
    get "/", PageController, :home
    live "/test", TestLive
  end

Create a LiveView module

/lib/deploy_to_render_web/live/test_live.ex
defmodule DeployToRenderWeb.TestLive do
use DeployToRenderWeb, :live_view
def mount(_params, _session, socket) do
{:ok, socket}
end
def render(assigns) do
~H"""
I'm a LiveView!
"""
end
end

Start the server with mix phx.server and visit https://localhost:4000/test to see a simple rendered LiveView. Commit to your code repository to trigger a deployment on Render.

Now visit the same URL on your newly deployed web service and you should see an alert at the top right that says “We can’t find the internet Attempting to reconnect”. All you have to do to fix this is to add a single line to runtime.exs:

/config/runtime.exs
  config :deploy_to_render, DeployToRenderWeb.Endpoint,
    check_origin: :conn,
    url: [host: host, port: 443, scheme: "https"],
    ...

You also need to configure your application to use proxy headers in prod.exs:

/config/prod.exs
import Config
config :deploy_to_render,
DeployToRenderWeb.Endpoint,
cache_static_manifest: "priv/static/cache_manifest.json",
force_ssl: [rewrite_on: [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto]]

You should now be able to use LiveView on Phoenix applications deployed to Render!

Troubleshooting

  1. bash: ./build.sh: No such file or directory in the deploy logs when deploying on Render.

    Make sure you check in and push build.sh to your code respository. Make sure that build.sh is in the root of the application directory. (Read Create a build script)

  2. (RuntimeError) environment variable DATABASE_URL is missing. in the deploy logs when deploying on Render.

    This guide assumes that you’re going to connect to a database. Make sure you create a PostgreSQL database on Render and set the DATABASE_URL environment variable as described above.

    NOTE: The free instance type is deleted after a month, so make sure that it’s still up and running if you see this error once you’ve deployed the application a while ago.

  3. ”We can’t find the internet Attempting to reconnect” in a red alert box in the top right when visiting a LiveView URL.

    You’ll also see Phoenix LiveView constantly refreshing. This only happens when you use a custom domain on Render. (Read Fix LiveViews to fix this issue)

  4. (UndefinedFunctionError) function App.Release.migrate/0 is undefined (module App.Release is not available) in the deploy logs when deploying on Render.

    Make sure to use your application’s <MODULE_NAME> in release.ex. (Read Run Ecto migrations on every deploy)

  5. (ArgumentError) could not fetch application environment :ecto_repos for application :deploy_to_render because the application was not loaded nor configured in the deploy logs when deploying on Render.

    Make sure to use your application’s <APP_NAME> in release.ex. (Read Run Ecto migrations on every deploy)

Reference

  1. Render website
  2. Guide to getting up and running with Phoenix
  3. Guide to deploying a Phoenix Elixir with releases
  4. Render’s guide to deploy a Phoenix application with releases on Render
  5. Render’s guide to adding a custom domain
  6. Render’s guide to configuring DNS
  7. Render’s docs on port binding
  8. Render’s docs on setting your Elixir and Erlang Versions
  9. Render’s list of supported Elixir versions