Hi jesse,
Thanks - no, I didn’t get it going - you’re right the server appears to not be listening on port 8080, and I couldn’t find any reason for that. The strange thing is, the Phoenix 1.2 app I had working with Distillery and Edeliver uses the same port, nginx reverse proxy config…
To try to find out what’s wrong, I’ve decided to start from scratch with the default Phoenix 1.3 install, and see if you and others can help me spot what I’m doing wrong.
-
The app is called phx1p3.
-
I want to build the app for deployment with erts included, not install erts on the production server.
-
My production server user for the deployed app is called freebsd.
On dev machine
Install Phx 1.3 App
$ mix phx.new phx1p3
Fetch and install dependencies? [Yn] y
* running mix deps.get
* running mix deps.compile
* running cd assets && npm install && node node_modules/brunch/bin/brunch build
$ cd phx1p3
Generate new secret for app:
$ mix phx.gen.secret
Copy outputted secret into config/prod.secret.exs
Add elixir/phoenix packages
to mix.exs
for what my project needs eg bamboo for email:
$ vim mix.exs
Add:
{:bamboo, "~> 0.8.0"},
{:bamboo_smtp, "~> 1.2"},
{:secure_random, "~> 0.5.0"},
{:comeonin, "~> 2.6"},
{:edeliver, "~> 1.4.3"},
{:distillery, "~> 1.4.1"}
Install NPM packages:
$ cd phxp1p3/assets
$ npm install --save-dev copycat-brunch
$ npm install --save font-awesome
Update assets/brunch-config.js
for the installed NPM packages:
$ vim assets/brunch-config.js
Change:
// Configure your plugins
plugins: {
babel: {
// Do not use ES6 compiler in vendor code
ignore: [/vendor/]
}
},
to:
// Configure your plugins
plugins: {
babel: {
// Do not use ES6 compiler in vendor code
ignore: [/vendor/]
},
copycat: {
fonts: ["node_modules/font-awesome/fonts"]
}
}
Install Phoenix and NPM packages/dependencies:
$ mix deps.get —all
Create project database:
$ mix ecto.create
Test app works locally:
$ mix phx.server
Git
$ git init
Add Edeliver’s releases directory to .gitignore
$ echo ".deliver/releases/" >> .gitignore
$ git add .
$ git commit -m "initial commit"
Edit prod.exs
Replace load_from_system_env: true
, with http: [port: 8080]
, and uncomment the line config :phoenix, :serve_endpoints, true
.
$ vim config/prod.exs
use Mix.Config
# For production, we often load configuration from external
# sources, such as your system environment. For this reason,
# you won't find the :http configuration below, but set inside
# Phx1p3Web.Endpoint.init/2 when load_from_system_env is
# true. Any dynamic configuration should be done there.
#
# Don't forget to configure the url host to something meaningful,
# Phoenix uses this information when generating URLs.
#
# Finally, we also include the path to a cache manifest
# containing the digested version of static files. This
# manifest is generated by the mix phx.digest task
# which you typically run after static files are built.
config :phx1p3, Phx1p3Web.Endpoint,
# load_from_system_env: true,
http: [port: 8080],
url: [host: "myapp.tld", port: 80],
cache_static_manifest: "priv/static/cache_manifest.json"
# Do not print debug messages in production
config :logger, level: :info
# ## SSL Support
#
# To get SSL working, you will need to add the `https` key
# to the previous section and set your `:url` port to 443:
#
# config :phx1p3, Phx1p3Web.Endpoint,
# ...
# url: [host: "example.com", port: 443],
# https: [:inet6,
# port: 443,
# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
#
# Where those two env variables return an absolute path to
# the key and cert in disk or a relative path inside priv,
# for example "priv/ssl/server.key".
#
# We also recommend setting `force_ssl`, ensuring no data is
# ever sent via http, always redirecting to https:
#
# config :phx1p3, Phx1p3Web.Endpoint,
# force_ssl: [hsts: true]
#
# Check `Plug.SSL` for all available options in `force_ssl`.
# ## Using releases
#
# If you are doing OTP releases, you need to instruct Phoenix
# to start the server for all endpoints:
#
config :phoenix, :serve_endpoints, true
#
# Alternatively, you can configure exactly which server to
# start per endpoint:
#
# config :phx1p3, Phx1p3Web.Endpoint, server: true
#
# Finally import the config/prod.secret.exs
# which should be versioned separately.
import_config "prod.secret.exs"
Install Distillery and Edeliver
Edeliver
Create directory and config file for Edeliver:
$ mkdir .deliver
$ touch .deliver/config
$ vim .deliver/config
Paste and edit config from Deploy Early and Often: Deploying Phoenix with Edeliver and Distillery (Part Two), and change:
-
prefix phoenix. to phx. (for Phoenix 1.3).
-
add change of path for change in Phoenix 1.3 directory eg cd '$BUILD_AT/assets'
for NPM install.
APP="phx1p3"
BUILD_HOST="xxx.xx.xx.xx"
BUILD_USER="freebsd"
BUILD_AT="/tmp/edeliver/$APP/builds"
#RELEASE_DIR="/tmp/edeliver/$APP/builds/_build/prod/rel/$APP"
# prevent re-installing node modules; this defaults to "."
GIT_CLEAN_PATHS="_build rel priv/static"
STAGING_HOSTS="xxx.xx.xxx.xxx"
STAGING_USER="freebsd"
TEST_AT="/home/freebsd/staging"
PRODUCTION_HOSTS="xxx.xx.xx.xx"
PRODUCTION_USER="freebsd"
DELIVER_TO="/home/freebsd/www"
# For *Phoenix* projects, symlink prod.secret.exs to our tmp source
pre_erlang_get_and_update_deps() {
local _prod_secret_path="/home/freebsd/prod.secret.exs"
if [ "$TARGET_MIX_ENV" = "prod" ]; then
__sync_remote "
ln -sfn '$_prod_secret_path' '$BUILD_AT/config/prod.secret.exs'
"
fi
}
pre_erlang_clean_compile() {
status "Running phx.digest" # log output prepended with "----->"
__sync_remote " # runs the commands on the build host
# [ -f ~/.profile ] && source ~/.profile # load profile (optional)
source ~/.profile
# echo \$PATH # check if rbenv is in the path
set -e # fail if any command fails (recommended)
cd '$BUILD_AT' # enter the build directory on the build host (required)
# prepare something
mkdir -p priv/static # required by the phx.digest task
cd '$BUILD_AT/assets'
npm install
./node_modules/brunch/bin/brunch build --production
# run your custom task
cd '$BUILD_AT'
APP='$APP' MIX_ENV='$TARGET_MIX_ENV' $MIX_CMD phx.digest $SILENCE
"
}
Distillery
Create Distillery rel
directory and config file:
$ mix release.init
Edit the generated Distillery config file to set:
-
default_environment: :prod
-
dev environment to not build erts
-
prod environment to build erts
$ vim rel/config.exs
# Import all plugins from `rel/plugins`
# They can then be used by adding `plugin MyPlugin` to
# either an environment, or release definition, where
# `MyPlugin` is the name of the plugin module.
Path.join(["rel", "plugins", "*.exs"])
|> Path.wildcard()
|> Enum.map(&Code.eval_file(&1))
use Mix.Releases.Config,
# This sets the default release built by `mix release`
default_release: :default,
# This sets the default environment used by `mix release`
default_environment: :prod
# For a full list of config options for both releases
# and environments, visit https://hexdocs.pm/distillery/configuration.html
# You may define one or more environments in this file,
# an environment's settings will override those of a release
# when building in that environment, this combination of release
# and environment configuration is called a profile
environment :dev do
# If you are running Phoenix, you should make sure that
# server: true is set and the code reloader is disabled,
# even in dev mode.
# It is recommended that you build with MIX_ENV=prod and pass
# the --env flag to Distillery explicitly if you want to use
# dev mode.
set dev_mode: false
set include_erts: false
set cookie: :"@ts,~1]ld]%|CJkB1El@r$mgpcWloZ$kK15JnxJAJ!{XuwwBRQp/%iAV4qW?Ggse"
end
environment :prod do
set include_erts: true
set include_src: false
set cookie: :"|P[:(]X=SI_bRRm7[V,$=_UKFneJW3$Q[z*BrfyjmR..`lTIFao($R;*RRcLXgPQ"
end
# You may define one or more releases in this file.
# If you have not set a default release, or selected one
# when running `mix release`, the first release in the file
# will be used by default
release :phx1p3 do
set version: current_version(:phx1p3)
set applications: [
:runtime_tools
]
end
On Staging or Production Server
Create prod.secret.exs
in deployment user’s home directory:
$ touch ~/prod.secret.exs
Copy contents of config/prod.secret.exs
on dev machine to the above file on the server via clipboard.
Create Postgresql Database and Assign User Privileges
Switch to Postgresql user
$ sudo -i -u postgres
Log into Postgresql console
$ psql
postgres=# CREATE DATABASE phx1p3_prod;
Create user with same name as the user account running the Phoenix app:
postgres=# CREATE USER freebsd WITH PASSWORD 'password';
Grant that user privileges to new database
postgres=# GRANT ALL PRIVILEGES ON DATABASE phx1p3_prod TO freebsd;
Quit Postgresql and exit user to server’s deployment user:
postgres=# \q
$ exit
Build, Deploy, and Start on Production Server
$ mix edeliver build release production --verbose
$ mix edeliver deploy release to production --verbose
$ mix edeliver start production
Nginx Server Config
Relevant part of nginx.conf:
$ cat /usr/local/etc/nginx/nginx.conf
#Production Server hosting Phx1p3 app
upstream phx1p3 {
server xx.xxx.xx.xxx:8080;
}
#Nginx Reverse Proxy Server's redirect request to www.myapp.tld; on port 80 to http:/myapp.tld; on port 80
server {
listen 80;
server_name www.myapp.tld;
return 301 $scheme://myapp.tld$request_uri;
}
server {
listen 80;
server_name myapp.tld;
location /{
try_files $uri @proxy;
}
location @proxy {
# include proxy_params;
proxy_redirect off;
proxy_pass http://phx1p3;
}
}