Setting up Meteor 1.4+, Meteor Up, LetsEncrypt SSL, and Nginx in 15 minutes

Probably the simplest way to deploy a single instance of Meteor. Once you’ve done this once it takes about 15 minutes to set up a new deployment.

Install and set up kadirahq/meteor-up. We’ll be setting up mup to allow for multiple environments, if you want to deploy a develop, release, master, etc. You can find a sample mup.js on kadirahq/meteor-up. You’ll want to deploy to port 3000 or some other not 80 port since we’re going to use Nginx as a reverse proxy. Also note to use abernix/meteord:base as your docker image if you’re using Meteor 1.4+. It’s not good practice to check your mup file into VCS, it will contain your deployment user’s password.

# in the root of your meteor project
sudo npm install -g mup
mkdir .mup
touch .mup/my_deployment.mup.js
touch .mup/my_deployment.settings.json
# fill out your mup.js and settings.json files, we'll use meteorapp as our deployment user

Set up an Ubuntu 16.x virtual machine in your favorite host. Create a meteorapp account that we’ll use for deployments. Be sure to choose a sufficiently strong password for the account.

sudo apt update
sudo adduser meteorapp
sudo adduser meteorapp sudo

Update the /etc/sudoers file to allow password-less sudo for mup.

sudo nano /etc/sudoers
# add this line to before "# See sudoers(5) for more information on "#include" directives:"
meteorapp ALL=(ALL:ALL) NOPASSWD:ALL

Install Nginx and LetsEncrypt.

sudo apt install nginx
sudo service nginx stop
sudo apt install letsencrypt

Generate a LetsEncrypt certificate for your domain. Add it to the Nginx config.

sudo letsencrypt certonly -d my_domain.tld
sudo nano /etc/nginx/sites-enabled/default
# add the following information to the top to rewrite http to https
server_tokens off;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
rewrite ^(.*) https://$host$1 permanent;
}
# add this block for each Meteor instance you'll run on the vm, be sure to update the domain (and the port if you're not using 3000)

server {
server_name my_domain.tld www.my_domain.tld;
listen 443;
ssl on;
ssl_certificate /etc/letsencrypt/live/my_domain.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/my_domain.tld/privkey.pem;
# performance enhancement for SSL
ssl_stapling on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
# safety enhancement to SSL: make sure we actually use a safe cipher
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK';
# config to enable HSTS(HTTP Strict Transport Security) https://developer.mo$
# to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_str$
add_header Strict-Transport-Security "max-age=31536000;";
# If your application is not compatible with IE <= 10, this will redirect vi$
# This works because IE 11 does not present itself as MSIE anymore
if ($http_user_agent ~ "MSIE" ) {
return 303 https://browser-update.org/update.html;
}
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # allow websockets
proxy_set_header Connection $connection_upgrade;
# proxy_set_header X-Forwarded-For $remote_addr; # preserve client IP
}
}

Add a crontab entry to automatically renew your cert.

sudo crontab -e
# add the crontab line
30 2 * * 0 service nginx stop && /usr/bin/letsencrypt renew >> /var/log/le-renew.log && service nginx start # renew if required at 2:30 am on Mondays

Using the newer certbot? You can just call:

0 2 * * * certbot renew --pre-hook "service nginx stop" --post-hook "service nginx start"

Start Nginx back up again.

sudo service nginx start

Set up and deploy your Meteor app on your local machine.

mup setup --config .mup/my_deployment.mup.js --settings .mup/my_deployment.settings.json
mup deploy --config .mup/my_deployment.mup.js --settings .mup/my_deployment.settings.json
mup deploy --config

Sidenote: Getting mup failures on the Verifying Deployment step? Try bumping deployCheckWaitTime to 120 in your mup.js file.

You’re done!

Next steps? Set up continuous deployment by triggering mup deploy on a push to your VCS.