Deploy Phoenix 1.6 / Elixir 1.3 on Render.com: A Working Guide
Spent almost a week fixing errors after errors trying to deploy my Phoenix/Elixir App on render.com. So I decided to write my journey to hopefully help noobs like me.
Note that this blog is for noobs (like me), so I had to really tell what needs to be done (dumb down things), kinda like what I want some blogs to be written, for me to understand it well enough so I don’t have to experience this again 😂 . If you think you’re too smart for this, I’m sorry and if there’s anything wrong with how I understand things, feel free to correct and enlighten me 🙏. Anyways, let’s get started.
Preparing your Phoenix/Elixir App for Deployment
You can follow this tutorial: Deploy a Phoenix App with Mix Releases and if everything works, you can skip this portion.
BUT if it doesn't (Just like mine) there are things I needed to change to make it work.
- First is the
build.sh
, here’s mine:
#!/usr/bin/env bash# exit on error
set -o errexit# Initial setup
mix deps.get --only prod
MIX_ENV=prod mix compile# Compile assets
npm install --prefix ./assets
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/alvin_and_eloisa/bin/alvin_and_eloisa eval "Release.migrate"# Run seeds
MIX_ENV=prod mix run priv/repo/seeds.exs
As you notice, I'm compiling my assets using both npm and mix, For some reason, when using mix assets.deploy
alone, esbuild is not compiling my app.css
on production, and when I'm using npm
my JS is not compiling, So I figured I can use both to compile both and when I tried it, It worked! 😂
Also, you may want to comment the Run seed
block after running it successfully the first time, to avoid duplicate data on your production database.
- Next is
mix.exs
.
defp aliases do
[
setup: ["deps.get", "ecto.setup", "cmd --cd assets npm install"],
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
"assets.deploy": [
"cmd --cd assets npm run deploy",
"esbuild default --minify",
"phx.digest"
]
]
end
Had to edit my alias for assets.deploy
to do npm run deploy
which will run the deploy script on our package.json below.
package.json
, add thescripts
block to compile my app.css, I’m using TailwindCSS by the way.
{
"scripts": {
"deploy": "NODE_ENV=prod tailwindcss --postcss --input=css/app.css --output=../priv/static/assets/app.css --minify"
},
"dependencies": {
"cogo-toast": "2.0.1",
"onmount": "^1.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-phoenix": "file:../deps/react_phoenix"
},
"devDependencies": {
"@babel/preset-env": "^7.16.4",
"@babel/preset-react": "^7.16.0",
"autoprefixer": "^10.4.0",
"postcss": "^8.4.5",
"postcss-cli": "^8.3.1",
"postcss-import": "^14.0.2",
"tailwindcss": "^3.0.7"
}
}
Setting up Environment Variables
On the render dashboard, select the web service you just created and go to the Environment tab and click the Add Environment Variable
button to add environment variables.
Database Url
Create a variable named DATABASE_URL
, then go to the database service you created, and copy the Internal Connection String
and paste it on the value
textbox.
Secret Key Base
Create a variable named SECRET_KEY_BASE
, then in your terminal, run mix phx.gen.secret
, copy the result and paste it on the value
textbox.
Customizing Elixir and Erlang/OTP versions
You can check here for the list of Elixir and Erlang versions supported by render, mine are:
- ELIXIR_VERSION:
1.13.1
- ERLANG_VERSION:
24.1.7
You need to make sure your versions are correct and ideally equal to your local setup, I’ve had issues with these when deploying my app on production not realizing that the default elixir version on render is 1.9.4.
Adding Custom Domain with hostinger
Once you got your domain on hostinger, go to your web service settings (on render) and click Add Custom Domain
. Input the domain you bought and after that will something like this below:
It is basically telling you to add these DNS records pointing your domain to the render web server, so let's do that. Inside your hostinger account, go to the DNS/Nameservers
tab of the domain you just bought and add a DNS record.
Hostinger doesn’t have ANAME and ALIAS records so I ended up adding just these three below. After you are done, go back to the settings page of your web service (on render) and click verify, it may take a couple of minutes to verify but if for some reason it fails (like mine), delete the record on hostinger and add it again (that’s what I did).
Types Name Pointing to
CNAME www yourwebsite.onrender.com
CNAME * yourwebsite.onrender.com
A @ 216.24.57.1
NOTE: When your custom domain was added to your web service make sure to also add it to your check_origin
in prod.exs
.
config :your_app, YourAppWeb.Endpoint,
url: [host: System.get_env("RENDER_EXTERNAL_HOSTNAME") || "localhost", port: 80],
check_origin: [
"https://yourdomain.com/",
"//yourdomain.com"
],
cache_static_manifest: "priv/static/cache_manifest.json"
this will prevent you from having some WebSocket issues and DB Connection errors like this ⬇️
Conclusion
Considering that this is the first time I’ve deployed a Phoenix/Elixir App, I can’t say that I enjoyed it BUT it’s a great experience and it is sure as hell a satisfying one, especially when you figured all things out and you see your website working properly, I had a blast!!
You can check the issues I’ve encountered here and feel my pain throughout the whole process.
If you find this helpful, I’d appreciate a coffee 😊
Happy Coding!!