Michael Josephson

Below are some more detailed notes to accompany the talk too. If you give any of this a try and need more details at all please drop me a line.

Conventions

The instructions assume a user will be added to the server that will be used for running the hosted applications. In these notes that user is called deploy.
The application installation instructions use an app name of demoapp which can be replaced as appropriate.
Commands that can be run as the deploy user are prefixed with a $ whereas commands that need to be run as root are prefixed with a #.
Instructions are based on using Ubuntu 16.04 LTS

Server set up

Add a separate user that will be used for running the hosted applications:

# useradd deploy

Set up sudo, this sets things up so the deploy user can run commands via sudo without needing to enter a password, this is required for deployments to run without user intervention:

Rerun .bash_profile to avoid needing to log out and log back in again to pick up changes above: $ source ~/.bash_profile

Add rbenv-vars plugin to allow a .rbenv-vars file in each application folder to be used to set environment variables for that app: $ git clone https://github.com/rbenv/rbenv-vars.git $(rbenv root)/plugins/rbenv-vars

Create a bare clone of the app repo (i.e. contains a copy of all of the git history but without a working copy): $ git clone --bare https://github.com/mikej/demoapp.git ~/demoapp_repo

Create the folder to contain the actual running copy of the app: $ mkdir ~/demoapp

Add .rbenv-vars file in ~/demoapp. This file is used by the rbenv-vars plugin to set any necessary environment variables for the app:
RAILS_ENV=production
SECRET_KEY_BASE=
DATABASE_USER=demoapp_
DATABASE_PASSWORD=
If your app needs to be able to send emails then this is also the place you'd add any SMTP credentials etc.

Copy/upload the post-receive hook and update-app.sh script into the bare repo folder from step 4 (⚠️ ensure post-receive is readable and executable by deploy user)

Copy/upload a systemd script for your app into /lib/systemd/system and symlink it into /etc/systemd/system/multi-user.target.wants: $ sudo ln -s /lib/systemd/system/demoapp.service /etc/systemd/system/multi-user.target.wants/demoapp.service

-a webroot indicates to use webroot authentication: a file is placed in the a folder called .well-known under the web-root path. On running the letsencrypt command, a copy of this file will be requested by Let’s Encrypt to verify ownership of the domain specified.

Create an nginx config snippet file in /etc/nginx/snippets that is specific to this certificate e.g. /etc/nginx/snippets/ssl-demoapp.josephson.org.conf:

Create a Configuration Snippet with Strong Encryption Settings. This is a file e.g. /etc/nginx/snippets/ssl-params.conf that can be shared across all your apps, containing a good set of SSL defaults to use for nginx. This is using the recommendations by Remy van Elst on the Cipherli.stsite. You can read more about his decisions regarding the Nginx choices here.

Update the config file in /etc/nginx/sites-available to support SSL and to redirect non-https requests to the corresponding https URL e.g. using config like this.

Restart nginx: $ sudo systemctl restart nginx

Let’s Encrypt certificates are valid for 90 days, but it’s recommended that you renew the certificates every 60 days to allow a margin of error using $ sudo letsencrypt renew. This can be automated by setting up a daily cron task - if there are no certificates due for renewal at the time of running then letsencrypt will indicate this and exit without making any changes.