Deploying Phoenix with Git
last updated: July 14, 2015
I have always enjoyed the experience of doing Heroku-like
git push deploys and just
recently set up deployment for my Phoenix application. It turns out, setting this
up doesn't take too much time and immediately pays dividends. In this article
I'll show you how to set up your server so that it automatically does the
- Deploy the app to the server
- Install all dependencies
- Build production assets
- Run all Ecto migrations
Throughout this tutorial we will assume a VPS that uses Ubuntu.
Configure Server for the Application
Since we are not building a release, we will need to install the dependencies so that Phoenix can run its mix tasks and build its assets. A default Phoenix app will need the following installed:
wget http://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i erlang-solutions_1.0_all.deb sudo apt-get update sudo apt-get install elixir nodes-legacy npm postgresql postgresql-contrib npm install -g brunch sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
brunch since they come as the default build tool for Phoenix.
We also redirect connections from
port 80 to
port 8080 since we will have the
app listen to
Server as Remote Git Repository
For us to be able to push our code to the server, we will need to configure it as a remote repository. To do this, you will need to do the following from your VPS:
cd /var mkdir repo && cd repo mkdir app.git && cd app.git git init --bare
From our local machine, we could push code to the server by doing:
git remote add production ssh://user@yourserver/var/repo/app.git git push production master
Inspecting your VPS's
/var/repo/app.git directory, you will see that there are no source
files there from your app. This is due to the
--bare option we passed earlier. Remote
repositories are usually bare repositories since they are meant to accept code pushes
from different collaborators. Having a working directory of your app in the bare repo will
just lead to conflicts.
So how do we deploy the app when we don't have its source available after a
The Post-Receive Hook
Git provides hooks that get run after
certain actions. The hook we want is the
post-receive hook since it gets triggered every
time your server receives a push.
In the VPS, we do the following:
mkdir -p /var/www/app.com cd /var/repo/app.git/hooks vim post-receive
Feel free to use whatever editor you like. From the editor, type:
#!/bin/bash git --work-tree=/var/www/app.com --git-dir=/var/repo/site.git checkout -f cd /var/www/app.com npm install && node_modules/bower/bin/bower install && node_modules/brunch/bin/brunch build --production if [ $PORT = 8080 ]; then PORT=8888 mix do deps.get, deps.compile, phoenix.digest, ecto.migrate && PORT=8081 elixir --detached -S mix phoenix.server && sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8081 sleep 5 && sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 previous_port=$(sudo lsof -t -i:8080) if [ -n $previous_port ]; then sudo kill $previous_port fi echo 'export PORT=8081' > /etc/profile.d/PORT.sh else PORT=8888 mix do phoenix.digest, deps.get, deps.compile, ecto.migrate && PORT=8080 elixir --detached -S mix phoenix.server && sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 sleep 5 && sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8081 previous_port=$(sudo lsof -t -i:8081) if [ -n $previous_port ]; then sudo kill $previous_port fi echo 'export PORT=8080' > /etc/profile.d/PORT.sh fi
git line checks out the source files of our app to the
We can then serve our app from that directory later on.
The last line starts our app on
port 8080 in detached mode if it hasn't started yet. You
may change port you want your app to listen on.
The other commands will install all of our app's dependencies, build all its assets, and
run all the Ecto migrations every time we
git push to the repo.
One last thing:
chmod +x /hooks/post-receive
The above command makes sure we can execute the
Configure the Database
We need to create the production database for our app and configure it in Phoenix so Ecto can access it.
sudo -u postgres createuser --superuser $USER sudo -u postgres psql
Using the postgres role, we created a new role with the same name as your login name. We then access psql as the postgres user to set the password for the new role. Take note of the login name and the new password since we will use this later for configuring Ecto.
postgres=# \password $USER
Then we can create the production database.
Next, we configure Ecto so it can access our production database.
The file will look like:
config :app, App.Repo, adapter: Ecto.Adapters.Postgres, username: "your_login_name", password: "your_password", database: "app", size: 20
database options with new ones we created earlier
Test and Deploy
Congratulations! You can now do
git push deploys to your server with:
git push production master
You should be able to see all the installs, builds and migration commands running after
If you have no new code to push yet and want to test if your
post-receive hook works,
you can do below from your VPS:
cd /var/repo/app.git git log -2 --format=oneline --reverse
Get the sha for the 2 commits and replace them with
echo "$SHA1 $SHA2 master" | ./hooks/post-receive
Voila! You get to test if your
post-receive hook works without pushing any code!
I found this deployment strategy to be simple to set up and wrap my head around. It made it easy to ship code to production without the overhead of learning another tool.
Feel free to share your deployment strategy and let me know if you have any questions or feedback. Looking forward to hearing from you!
- How To Set Up Automatic Deployment with Git with a VPS
- Phoenix Advanced Deployment
- Git on the Server
- Setting up Push-to-Deploy with git