Tutorial

Step by step guide to deploy Phoenix with LiveView and Tailwind on Render

This post was updated 27 Mar

deploy liveview render

Sooner or later there comes a time when you want to deploy your Phoenix application. Render is a great PaaS service that offers easy deployment for a great value. I have prepared both the Phoenix Boilerplate and the SaaS Starter Kit to make this deployment as easy as possible. This also works with Phoenix LiveView and Esbuild as well as Tailwind.

They already have a deploy guide on their docs section but it is for an older version of Phoenix and it doesn’t use Ecto and Postgres.

Step 1 - Prepare your Phoenix app

As I mentioned above, I have already made some legwork and included almost all of the changes needed in the boilerplates and the SaaS Starter Kit. However, if you have not used it, here we go.

In the root of my app, I need to create a build.sh file. Render will use this file when doing the deploy.

# Initial setup
mix deps.get --only prod
MIX_ENV=prod mix compile
MIX_ENV=prod mix assets.deploy
# Build the release and overwrite the existing release directory
MIX_ENV=prod mix release --overwrite
# Run migrations
_build/prod/rel/my_app/bin/my_app eval "Release.migrate"

NOTE - Replace my_app with your app name.

I need to make sure the script is executable to be able to run it:

chmod a+x build.sh

Also, as you can see above in the script, there is a function call here: Release.migrate. This module takes care of running the migrations on every deploy. That module looks like this:

# lib/my_app/release.ex
defmodule MyApp.Release do
@app :my_app
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

NOTE - Once again, change :my_app to your app name.

I need to set the release to be a standalone server. For that I need to uncomment the line:

# config/runtime.exs
# Make sure to uncomment
config :my_app, MyAppWeb.Endpoint, server: true

Next step is to change the prod config so I can connect with another domain than example.com. For that I need to change the line url: [host: "example.com", port: 80]:

# config/prod.exs
url: [host: System.get_env("RENDER_EXTERNAL_HOSTNAME") || "localhost", port: 80],

RENDER_EXTERNAL_HOSTNAME is the temporary URL used for testing. When you are ready to point your domain to Render, update this with your domain name. But for now, this will do.

Next up: Mix is not available at runtime, so code like Mix.env() won’t work in production. Instead use:

# Does work
Application.get_env(:my_app, :env)

To make this work, I need to add this in the config:

# config/config.exs
config :my_app, :env, Mix.env()

This is needed in the router file where the mailbox route is. The mailbox should only be visible in the dev environment — same for the live dashboard if you don’t want that in production. So change that in the router:

# lib/my_app_web/router.ex
Application.get_env(:my_app, :env) == :dev

Ok, that should be all the changes needed. If I don’t already have a GitHub repo, I need to create one and push these changes.

Step 2 - Create a Render app

The obvious first substep here is to create a Render account.

For simplicity, sign in with GitHub or GitLab. I will need that integration later to let Render get access to the private repository I want to deploy. With this done, and maybe adding a credit card, I can now create the app.

Create database

Since my app needs a database to deploy, it makes sense to create the database first. Currently, you can start with a database for as low as $7 per month.

The name of the database should be my_app_prod by convention. Then pick the region and Postgres version and leave the rest as it is.

After the database is created I can copy the internal connection URL. I will need that as an environment variable for the Phoenix app.

Create Phoenix app

Create a new web service from the menu at the top. Render won’t have access to my repos by default, so I first need to click the connect link to grant access through GitHub to one or more repos.

When that is done and I have picked a repo, I can name and set up the app. The starter plan is also $7 per month. The important things here are to get the build and start commands right.

Next, I need to set the environment variables. They are at the bottom of the page under advanced. The ones needed may vary by application, but at minimum:

SECRET_KEY_BASE= # run mix phx.gen.secret in your console
DATABASE_URL= # from the database internal connection string
ELIXIR_VERSION=1.17.3 # check Render docs on available versions
# https://render.com/docs/elixir-erlang-versions#supported-elixir-versions

The last option before submitting the form is to make sure Auto Deploy is selected. That means Render will pull the latest version of either main, master, or a specific deploy branch. I just take the default settings and opt for auto-deploy on main.

Deployment

Not much to say here. This should now be set up to auto-deploy as soon as there is a new push to my main branch on GitHub. If there is a deploy failure, I get an email and the release is rolled back. Everything is very smooth.

Related Tutorials

NEW
Published 27 Mar

Adding Modals to Phoenix 1.8 with DaisyUI

In Phoenix 1.8, the built-in modal component was removed. Instead, Phoenix now encourages developers to use separate LiveView pages for new and edit..

Published 03 Apr - 2024

Set session values from LiveView

Working with session data can significantly improve the feel of web applications, making interactions feel more connected and dynamic. However, Phoe..