develop

How to deploy anything with Meteor-Up

I have grown to love the simplicity of deploying Meteor apps with Meteor-Up.

According to the website:

Meteor Up is a production quality tool to setup servers and deploy your Meteor apps to them

Its a fantastic tool to simplify deployment in my opinion.

I’ve noticed their plugins before, but have never tried to deploy anything other than a meteor project … so I decided to give it a try.

At first I attempted to use the mup-node plugin, but I quickly realised that since my app didn’t expose a listener on a port, like a typical express app, I wouldn’t be able publish it without failing at the verification step, so I changed tactics.

My next attempt was to use another plugin from their list: the mup-docker-deploy plugin. This one allows you to deploy from a Dockerfile, which translates into almost anything you might want to deploy. To generate my Dockerfile, I was lazy and captured the commands from my first try by running mup in verbose mode and copying the steps from the log. The command for verbose mode is:

DEBUG=mup* mup deploy --verbose

And I ended up with a Dockerfile like this:

FROM node:15.8.0
RUN mkdir -p /home/node/app || true
WORKDIR /home/node/app
ENV NODE_ENV=production
COPY ./package.json ./package.json
COPY ./package-lock.json ./package-lock.json
RUN npm install --unsafe-perm
COPY ./ ./
CMD [ "npm", "run", "start" ]

I followed the instructions from the README and configured my .deploy/mup.js file:

module.exports = {
  servers: {
    one: {
      host: 'my-domain.name',
      username: 'root',
      pem: '~/.ssh/id_rsa'
    }
  },

  app: {
    name: 'my-app-name',
    path: '../',
    type: 'docker-image',

    env: {
      PORT: process.env.PORT,
    },

    servers: {
      one: {},
    },
    // docker: {
	  //   imagePort: Number(process.env.IMAGE_PORT),
    // },
  },
  // proxy: {
  //   servers: {
  //     one: {},
  //   },
  //   domains: "my-app.my-domain.name",
  // },
  plugins: ['mup-docker-deploy']
}

The important bits are:

  • type: 'docker-image',
  • env: { PORT: <number> }, (an available port on the host)
  • plugins: ['mup-docker-deploy'] (this one was not obvious from the README file)
  • docker.imagePort if your app exposes a port from docker to be mapped to the host port, e.g. by reverse proxy

The last challenge I had was to send the environment variables to the docker container without storing any secrets in my code.

After trying a few things, I ended up with the following solution:

In my mup.js, I added the environment variables to the env section with their values set them from the host environment, like so:

module.exports = {
  ...,
  app: {
    ...,
    env: {
      SERVICE_KEY: process.env.SERVICE_KEY,
      SERVICE_SECRET: process.env.SERVICE_SECRET,
    }
  }
}

I then added a .env file in the root of my project containing the secrets.

NOTE: Remember to add .env to your .gitignore file to exclude it from source control.

Lastly, I added a script to my package.json to prep the environment before deploying:

{
  ...,
  "scripts": {
    "deploy": "cd .deploy && npx dotenv-cli -e ../.env mup deploy",
  }
}

Doing a deploy is now as simple as running npm run deploy. It will do the following:

  1. Change directory to the .deploy folder
  2. Make use of dotenv-cli to set up the environment variables from the .env file
  3. Run mup deploy and push the environment to your deployed container