Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Deploying a Meteor app to Digital Ocean

Deploying a Meteor app to Digital Ocean

Table of Contents

  1. Create an SSH Key
  2. Create a Digital Ocean account and droplet
  3. Connect to your server with SSH
  4. Create a new user and disable root access
  5. Give your new user sudo privileges and add SSH keys to his/her account
  6. Disable remote access to root account
  7. Test your SSH connection
  8. Set up your custom domain
  9. Add some swap space
  10. Set up SSL
  11. Ports and Redirects with nginx
  12. Install Meteor Up
  13. Visit your domain

Create an SSH Key

If you don't already have one, create an RSA key pair. In Terminal enter:

ssh-keygen -t rsa

It will then ask you to name the file. Leave it empty and hit enter to accept the defualt name, id_rsa.

Enter file in which to save the key (/demo/.ssh/id_rsa):

You'll then be asked to enter a passphrase. Again, leave it empty and hit enter.

Enter passphrase (empty for no passphrase):

Back to Top

Create a Digital Ocean account and droplet

Select Ubuntu for your operating system. For production apps, it's recommended that you spring for the $10/month set up. For a full walkthrough, read How To Create Your First DigitalOcean Droplet Virtual Server.

When you get to step to add your SSH key, go back to your terminal, copy the contents of your new key with:

cat ~/.ssh/ | pbcopy

Then, back on Digital Ocean, give the key a name and paste it into the space as shown below:

Back to Top

Connect to your server with SSH

Copy your IP address from your newly created droplet. In Terminal, enter the following with your IP:

ssh root@XXX.XXX.XX.XXX

You'll likely be prompted with the following question. Answer yes.

The authenticity of host 'XXX.XXX.XX.XXX (XXX.XXX.XX.XXX)' can't be established.
RSA key fingerprint is Xx:xX:Xx:XX:XX:Xx:XX:XX:XX:xX:XX:XX:Xx:Xx:xx:Xx.
Are you sure you want to continue connecting (yes/no)?

The following repsonse should be:

Warning: Permanently added 'XXX.XXX.XX.XXX' (RSA) to the list of known hosts.

If your connection is closed after this step, just repeat the ssh root@XXX.XXX.XX.XXX step. You are now logged into your server as the root user using an SSH key.

Back to Top

Create a new user and disable root access

At the prompt on your server (should look something like this root@dropletname:~# , enter adduser followed by the username you want the new user to have.

adduser username

You'll be asked to create a password. What you type will not show up on screen, but type your password and hit enter. Then retype and hit enter again:

Enter new UNIX password:
Retype new UNIX password:

SAVE YOUR PASSWORD somewhere safe!

Once you've created and confirmed your new password, you'll be asked a few questions:

Changing the user information for username
Enter the new value, or press ENTER for the default
    Full Name []:
    Room Number []:
    Work Phone []:
    Home Phone []:
    Other []:
Is the information correct? [Y/n] Y

You can leave these blank if you don't need this info for your new user.

Back to Top

Give your new user sudo privileges and add SSH keys to his/her account

gpasswd -a username sudo

And you also need to add NOPASSWD to your sudoers file. Open it with:

sudo visudo

Then, replace the line that says %sudo ALL=(ALL) ALL with


To save these edits, type Ctrl + X, then confirm by typing Y and hitting enter:

Now, switch to your new user with: The dash before the username makes sure that you actually log in as the new user.

su - username

Next, create a new folder called .ssh and then restrict its permissions with the following commands:

mkdir .ssh
chmod 700 .ssh

Using the nano editor, we can create and edit a new file called authorized_keys using the following command:

nano .ssh/authorized_keys

In another Terminal window or tab, copy your SSH key again with:

cat ~/.ssh/ | pbcopy

Back in the nano editor window on the server, paste in your key. You should see your key displayed, without wrapping like so:

ssh-rsa MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQXGukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63ilAkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlFL0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5kX6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2eplU9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=

To save these edits, type Ctrl + X, then confirm by typing Y and hitting enter:

Save modified buffer (ANSWERING "No" WILL DESTROY CHANGES) ? Y

Now restrict the permissions of the authorized_keys file with this command:

chmod 600 .ssh/authorized_keys

Return to the root user with:


Which should give you a prompt like the following to let you know that you are back on the root user:


Back to Top

Disable remote access to root account

Open the config file with:

nano /etc/ssh/sshd_config

Around 28 lines down this file, change PermitRootLogin value to:

PermitRootLogin no

Save the file as before with Ctrl + X, y and enter.

Exit and logout of your server session with:


Back to Top

Test your SSH connection

To test your connection with your new username and SSH key, simply execute:

ssh username@XXX.XXX.XX.XXX

This should automatically log you on to your server as username.

Back to Top

Set up your custom domain

Go to your domain registrar and update your nameservers to point to


This is what the iwantmyname settings look like:

Back on Digital Ocean add your domain name to your DNS records:

Now, create a new A record with the name of @ and your IP address like so:

Back to Top

Add some swap space

(Optional) Follow the instructions here: How to Add Swap

Set up SSL

From the prompt on you server, enter the following where yourserver is the name of your server:

openssl req -new -newkey rsa:2048 -nodes -keyout yourserver.key -out yourserver.csr

This will generate a series of questions. When asked for Common Name, make sure you enter a wildcar subdomain for your address *

Common Name (e.g. server FQDN or YOUR name) []: *

Now, print the contents of the new yourserver.csr file using:

cat ~/yourserver.csr

Copy the output with both the -----BEGIN CERTIFICATE REQUEST----- and -----END CERTIFICATE REQUEST----- tags. Create new file and save it as yourdomain.key.

Go purchase your wildcard SSL certificate. After purchasing, follow instructions to activate your cert with your yourserver.csr key. Be sure to select nginx as your web server. Wait for the emails and follow instructions.

Once you get the zipped file of certs, unzip them and at your yourdomain.key file to the folder.

First, run this command to remove the password you set on you key:

openssl rsa -in yourdomain.key -out yourdomain.nopass.key

This will generate a new file for you with a key with no password. Now, you'll need to combine them into one file using a the following command with your file names:

cat yourdomain.crt DomainValidationSecureServerCA.crt AddTrustCA.crt.crt AddTrustExternalCARoot.crt yourdomain.nopass.key > ssl.pem

Save the ssl.pem file for later (or move it to a directory called mup-your-project-name somewhere outside your app repo).

Back to Top

Ports and Redirects with nginx

Back on your server, run the following commands update your packages and install nginx:

sudo apt-get update
sudo apt-get install nginx

Answer yes, when asked if you want to continue. Now open the server block default config file with:

sudo nano /etc/nginx/sites-enabled/default

Next, paste the following block into the file and edit to match your domain name. You can redirect just the www subdomain to you non-www domain with, or use a wildcard:

# redirect www to non-www
server {
    listen 80;
    # to redirect all subdomains use * instead of
    return 301 $scheme://$request_uri;

Save the file as before with Ctrl + X, y and enter.

Now, let's forward our domain to a different port number since nginx is listening on port 80. We'll use port 3000. Create and edit a new file with:

sudo nano /etc/nginx/sites-enabled/

Then past this block into that file:

server {
    listen 80;
    access_log /var/log/nginx/;
    error_log /var/log/nginx/;
        location / {
            proxy_pass http://XXX.XXX.XX.XXX:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header X-Forwarded-For $remote_addr;


Save and exit this file. Next, we'll add a small security measure by hiding our nginx version number. Run,

sudo nano /etc/nginx/nginx.conf

Find and uncomment the line with

server_tokens off;

Save and exit this file. Then run:

sudo service nginx restart

To test the change, open a new terminal window and run:

curl -I

The top line should read HTTP/1.1 301 Moved Permanently.

Back to Top

Install Meteor Up

npm install -g mup

Now, create a directory called mup-your-project-name somewhere outside your app's repo, switch to it, and initialize mup.

mkdir ~/mup-your-project-name
cd ~/mup-your-project-name
mup init

This should create two files: mup.json and settings.json. Open mup.json and under the servers block, put in your details:

// Server authentication info
"servers": [
        // the domain linked to Digital Ocean
        "host": "",
        // the new user you created on the server
        "username": "username",
        // the SSH key you generated earlier
        "pem": "~/.ssh/id_rsa"

"ssl": {
    "pem": "./ssl.pem"

Then, a bit further down in the same file:

// Application name (No spaces)
"appName": "your-project-name",

// Location of app (local directory)
"app": "/path/to/the/app",

// Configure environment
"env": {
    "ROOT_URL": "",
    // any port other than 80 (the default)
    // because nginx is running on port 80
    "PORT": 3000

Now, within your ~/mup-your-project-name directory, setup your server by running

mup setup

After that's done, it should look something like this (make your command line look awesome):

Then deploy your app with:

mup deploy

If successful, your output should look like:

Visit your domain

That should do it. Your site should be live.

Back to Top


This comment has been minimized.

Copy link

@JMurilloM JMurilloM commented Jan 17, 2016

Hi, when i do CURL commando to prove my domain, the message appears on my terminal it is:

HTTP/1.1 200 OK
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
Date: Sun, 17 Jan 2016 13:22:38 GMT
Connection: keep-alive

This is correct? or i have a problem?


This comment has been minimized.

Copy link
Owner Author

@jamiewilson jamiewilson commented Jan 18, 2016

@JMurilloM that looks good to me.


This comment has been minimized.

Copy link

@trahn trahn commented Jan 30, 2016

Hi, Thx for the guide. :) But how can I make sure that SSL is always used, i.e. redirected to https?


This comment has been minimized.

Copy link

@JMurilloM JMurilloM commented May 2, 2016

@jamiewilson sorry for not answered quickly but it is ok, thanks


This comment has been minimized.

Copy link

@rpersaud rpersaud commented May 23, 2016

How would I make this work without https, or ssl? If I leave those steps out - I always get a 302 redirect when going to my domain, or domain:3000.


This comment has been minimized.

Copy link

@sunlee-newyork sunlee-newyork commented Jul 31, 2016

Thanks so much for the guide! It all went smoothly except for the mup setup step - it failed because python was not installed on the server - just wanted to point that out.

Also, I get an error for MongoDB Install step:

    gpg: requesting key 7F0CEB10 from hkp server
    gpg: key 7F0CEB10: public key "Richard Kreuter <>" imported
    gpg: Total number processed: 1
    gpg:               imported: 1  (RSA: 1)
    W: Signature by key 492EAFE8CD016A07919F1D2B9ECBEC467F0CEB10 uses weak digest algorithm (SHA1)
    debconf: unable to initialize frontend: Dialog
    debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.)
    debconf: falling back to frontend: Readline
    debconf: unable to initialize frontend: Readline
    debconf: (This frontend requires a controlling tty.)
    debconf: falling back to frontend: Teletype
    dpkg-preconfigure: unable to re-open stdin:
    sudo: stop: command not found
    sudo: start: command not found

Thanks so much again!!


This comment has been minimized.

Copy link

@mallarapusidhu mallarapusidhu commented Aug 2, 2016

What will be the path for my installed app ? I have deployed a reaction commerce instance as "root" user with "mup"and I couldn't locate it.


This comment has been minimized.

Copy link

@jm1200 jm1200 commented Aug 23, 2016

Does this still work for anyone? I get to the mup setup step and nothing happens. I have been reading that there seem to be some mup/mupx/node/meteor version issues and am wondering if that is the problem.


This comment has been minimized.

Copy link

@bogere bogere commented Jun 21, 2017

Thank you for this tutorial.. please help why is my nginx failing to restart

  • Restarting nginx nginx [fail]
    after i have created and saved this file.....
    sudo nano /etc/nginx/sites-enabled/
    i have also run this command to find out what is problem sudo nginx -t
    but getting this result.
    nginx: [emerg] "listen" directive is not allowed here in /etc/nginx/sites-enabled/default:5
    nginx: configuration file /etc/nginx/nginx.conf test failed

This comment has been minimized.

Copy link

@nicszerman nicszerman commented Aug 9, 2018

This guide assumes that the user has at least meteor and nodejs installed, and the project downloaded somewhere. It wasn't my case and now this isn't working, but because you never described all the prerequisites I have no idea if this caused something that's missing. Does someone know the prerequisites?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment