Demonstration of Elixir Phoenix, how to get it up and running, and how to deploy via Gigalixir.
For this demo, we will use this application: Demo Elixir Phoenix
If you prefer, you can create your own typical Elixir Phoenix application with your own typical Postgres database.
There are various ways to install:
Gigalixir is a hosting service that specializes in hosting Elixir Phoenix applications.
For docs: https://gigalixir.com/docs/
Install for macOS:
python -m pip install gigalixir --user --ignore-installed six
…
Make sure the executable is in your path, if it isn’t already.
echo "export PATH=\$PATH:$(python3 -m site --user-base)/bin" >> ~/.profile
source ~/.profile
Verify:
gigalixir version
1.10.0
For help:
gigalixir --help
If you're new to Gigalixir, then create your account:
gigalixir signup
GIGALIXIR Terms of Service: https://www.gigalixir.com/terms
GIGALIXIR Privacy Policy: https://www.gigalixir.com/privacy
Do you accept the Terms of Service and Privacy Policy? [y/N]: y
Email: …
If you already use Gigalixir, then sign in:
gigalixir login
Email: [email protected]
Password:
Would you like us to save your api key to your ~/.netrc file? [Y/n]: Y
Logged in as [email protected].
Verify:
gigalixir account
Gigalixir has options for how to set up an app. For this demo, we will choose the simplest additional option, which is to do deployments via Mix (rather than Distillery which is more sophisticated) and by using the Gigalixir database free tier.
Append config/prod.exs
with:
config :demo_elixir_phoenix, DemoElixirPhoenixWeb.Endpoint,
http: [port: {:system, "PORT"}], # Possibly not needed, but doesn't hurt
url: [host: System.get_env("APP_NAME") <> ".gigalixirapp.com", port: 80],
secret_key_base: Map.fetch!(System.get_env(), "SECRET_KEY_BASE"),
server: true
config :demo_elixir_phoenix, DemoElixirPhoenix.Repo,
adapter: Ecto.Adapters.Postgres,
url: System.get_env("DATABASE_URL"),
ssl: true,
pool_size: 2 # Free tier db only allows 4 connections. Rolling deploys need pool_size*(n+1) connections where n is the number of app replicas.
Create buildpack files at the repo root:
echo "elixir_version=1.10.4" > elixir_buildpack.config
echo "erlang_version=23.0.2" >> elixir_buildpack.config
echo "node_version=14.5.0" > phoenix_static_buildpack.config
Optionally verify that your versions are in the list of version support by the buildpack here: https://github.com/HashNuke/heroku-buildpack-elixir#version-support
Create file .buildpacks
with the buildpacks you want, such as:
https://github.com/HashNuke/heroku-buildpack-elixir
https://github.com/gjaldon/heroku-buildpack-phoenix-static
https://github.com/gigalixir/gigalixir-buildpack-mix.git
Create the app with any name you want. There are some caveats: the name must be unique at Gigalixir, and can only contain letters, numbers, and dashes, and must start with a letter.
gigalixir create -n demo-elixir-phoenix
Created app: demo-elixir-phoenix.
Set git remote: gigalixir.
demo-elixir-phoenix
If you get either of these errors:
{"errors":{"unique_name":["has already been taken"]}}
{"errors":{"unique_name":["can only contain letters, numbers, and dashes and must start with a letter."]}}
Verify the app exists:
gigalixir apps
Output:
[
{
"cloud": "gcp",
"region": "v2018-us-central1",
"replicas": 0,
"size": 0.3,
"stack": "gigalixir-18",
"unique_name": "demo-elixir-phoenix"
}
]
Verify the git remote exists:
git remote -v | grep gigalixir
Output:
gigalixir https://git.gigalixir.com/demo-elixir-phoenix.git/ (fetch)
gigalixir https://git.gigalixir.com/demo-elixir-phoenix.git/ (push)
Try running the app with a server:
mix phx.server
Try running the app with Interactive Elixir:
iex -S mix phx.server
Optionally use PNPM or NPM for assets:
pnpm install --prefix assets
pnpm update --prefix assets
See https://gigalixir.readthedocs.io/en/latest/database.html#database-management
To create a free database:
gigalixir pg:create --free
A word of caution: Free tier databases are not suitable for production
and migrating from a free db to a standard db is not trivial.
Do you wish to continue? [y/N]: y
{
"app_name": "demo-elixir-phoenix",
"database": "f2a1aba9-72fb-42be-abb5-85ebdf2e887c",
"host": "postgres-free-tier-1.gigalixir.com",
"id": "14015dc3-1112-48ed-8059-0b1fcec7de7d",
"password": "pw-16eeecc3-9d81-494e-a701-0a109a8e93e3",
"port": 5432,
"state": "AVAILABLE",
"tier": "FREE",
"url": "postgresql://f2a1aba9-72fb-42be-abb5-85ebdf2e887c-user:pw-16eeecc3-9d81-494e-a701-0a109a8e93e3@postgres-free-tier-1.gigalixir.com:5432/f2a1aba9-72fb-42be-abb5-85ebdf2e887c",
"username": "f2a1aba9-72fb-42be-abb5-85ebdf2e887c-user"
}
Save the database URL as an environment variables, such as by creating a file .env.prod
:
APP_NAME="demo-elixir-phoenix"
DATABASE_URL="postgresql://f2a1aba9-72fb-42be-abb5-85ebdf2e887c-user:pw-16eeecc3-9d81-494e-a701-0a109a8e93e3@postgres-free-tier-1.gigalixir.com:5432/f2a1aba9-72fb-42be-abb5-85ebdf2e887c"
Load the environment:
source .env.prod
Verify:
echo $DATABASE_URL
postgresql://f2a1aba9-72fb-42be-abb5-85ebdf2e887c-user:pw-16eeecc3-9d81-494e-a701-0a109a8e93e3@postgres-free-tier-1.gigalixir.com:5432/f2a1aba9-72fb-42be-abb5-85ebdf2e887c
To list the databases:
gigalixir pg
To connect via psql console:
psql $DATABASE_URL
We choose to omit typical dot files and typical environment files.
Add these lines to the file .gitignore
:
# Ignore dot files by default, then accept specific files and patterns.
.*
!.gitignore
!.*.gitignore
!.*[-_.]example
!.*[-_.]example[-_.]*
!.*.gpg
# Ignore env files by default, then accept specific files and patterns.
env
env[-_.]*
!env[-_.]example
!env[-_.]example[-_.]*
!env[-_.]gpg
!env[-_.]*.gpg
Verify an alternative production environment is able to run locally:
APP_NAME=demo-phoenix-elixir \
SECRET_KEY_BASE="$(mix phx.gen.secret)" \
MIX_ENV=prod \
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/demo_elixir_phoenix_prod" \
PORT=4000 \
mix phx.server
If you get this error:
** (Mix) Could not compile dependency :telemetry
Then refresh the dependency, then retry.
mix deps.clean telemetry
mix deps.get
Build and deploy:
git push gigalixir master
If error:
Unable to select a buildpack
Then ensure you created a file .buildpacks
in the project root directory.
If error:
remote: warning: You appear to have cloned an empty repository.
Then double-check that you have committed all your files, and pushed them.
If error:
fatal: the remote end hung up unexpectedly
Then try using a larger buffer, such as:
git config --global http.postBuffer 100000000
If error:
npm ERR! Make sure you have the latest version of node.js and npm installed.
Then update node, such as:
npm install --prefix assets npm
npm update --prefix assets
If error:
npm ERR! Failed at the @ deploy script 'webpack --mode production'.
Then try using webpack locally:
npm install --prefix assets --save-dev webpack
npm install --prefix assets --save-dev webpack-dev-server
cd assets
$(npm bin)/webpack --mode production
Output:
Hash: 180d7e02464b28be7970
Version: webpack 4.43.0
Time: 1424ms
Built at: 07/22/2020 9:31:27 AM
Asset Size Chunks Chunk Names
../css/app.css 9.55 KiB 0 [emitted] app
../favicon.ico 1.23 KiB [emitted]
../images/phoenix.png 13.6 KiB [emitted]
../robots.txt 202 bytes [emitted]
app.js 2.25 KiB 0 [emitted] app
Entrypoint app = ../css/app.css app.js
[0] multi ./js/app.js 28 bytes {0} [built]
[1] ./js/app.js 490 bytes {0} [built]
[2] ./css/app.scss 39 bytes {0} [built]
[3] ../deps/phoenix_html/priv/static/phoenix_html.js 2.21 KiB {0} [built]
+ 2 hidden modules
Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/sass-loader/dist/cjs.js!css/app.scss:
Entrypoint mini-css-extract-plugin = *
[1] ./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./css/app.scss 745 bytes {0} [built]
[2] ./node_modules/css-loader/dist/cjs.js!./css/phoenix.css 10.4 KiB {0} [built]
+ 1 hidden module
If error:
[email protected] requires a peer of graphql@^14.7.0 || ^15.3.0 but none is installed.
You must install peer dependencies yourself.
Then install:
npm install --prefix assets graphql
If error:
remote: cp: cannot overwrite directory '/tmp/cache/node_modules/phoenix' with non-directory
remote: cp: cannot overwrite directory '/tmp/cache/node_modules/phoenix_html' with non-directory
Then you're likely trying to upgrade from an older version of your app, or Node, to a newer version, and the remote setup has changed. To solve this, tell the deployment to clean the node cache. Edit the file phoenix_static_buildpack.config
, add this one line below, do one successful deploy, then remove the line (or set to false):
clean_cache=true
Update Elixir dependencies:
mix deps.update --all
Update NPM:
npm update --prefix assets
Verify buildpack:
cat elixir_buildpack.config
Output such as:
elixir_version=1.10.4
erlang_version=23.0.2
Verify buildpack:
cat phoenix_static_buildpack.config
Output such as:
node_version=14.5.0