Simple self-hosted deployments & continuous delivery with Exoframe

yamalight profile image Tim Ermilov ・7 min read

There's a lot of services that allow you to simplify deployments of your projects to one command, e.g. Now.sh, Heroku, surge, etc. Most of them are quite amazing and provide a whole lot of neat features.
Unfortunately, almost all of them have one downside - you cannot use them with your own servers.
I'm guessing that majority of the time that's not much of a problem. But sometimes you just have a project or a setup where you need to use your own servers.
That's why I've built Exoframe - a self-hosted tool that allows simple one-command deployments using Docker.

What can Exoframe do?

Here's a basic list of features provided by Exoframe:

  • One-command project deployment
  • SSH key based auth
  • Rolling updates
  • Deploy tokens (e.g. to deploy from CI)
  • Automated HTTPS setup via letsencrypt *
  • Automated gzip compression *
  • Rate-limit support *
  • Simple access to the logs of deployments
  • Docker-compose support
  • Multiple deployment endpoints and multi-user support
  • Simple update procedure for client, server and Traefik
  • Optional automatic subdomain assignment (i.e. every deployment gets its own subdomain)
  • Swarm mode deployments
  • Complex recipes support (i.e. deploy complex systems in one command)

* Feature provided by Traefik

How does it work?

The way Exoframe works is pretty straightforward  -  the only thing you will need to install it is a server with Docker daemon.

Exoframe Server itself works in a pretty simple way - upon execution, it’ll spawn an instance of Traefik that’ll handle domain management, HTTPS, load balancing and all that stuff.
Then it simply gets packaged projects from Exoframe CLI and deploys them to your Docker daemon while configuring Traefik and Docker for you.
Authentication is done using private-public RSA keys, so normally you wouldn’t need any additional setup if you already can SSH into your server using your private key. If you are still using login and password for SSH, see this guide that’ll help you to switch to more secure key authentication.

Getting started in 3 simple steps

The following guide assumes that you already have a server that you can access via SSH, and you've already installed Docker on it.

Step 1: Install Exoframe Server

All you need to do to set the whole thing up is to execute Exoframe Server within your Docker daemon, like so:

docker run -d \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /path/to/exoframe-folder:/root/.exoframe \
  -v /home/user/.ssh/authorized_keys:/root/.ssh/authorized_keys:ro \
  -e EXO_PRIVATE_KEY=your_private_key \
  --label traefik.backend=exoframe-server \
  --label traefik.frontend.rule=Host:exoframe.your-host.com \
  --restart always \
  --name exoframe-server \

* make sure to change /home/user and /path/to/exoframe-folder to your local paths.

This command will start Exoframe Server, allow it to access your Docker via docker.sock, link config folder to your local folder to persist configuration upon restarts/updates/etc, link your authorized_keys files for authentication and finally  -  setup rules for Traefik. You can find a more detailed explanation of each line in the server documentation.

Step 2: Install Exoframe CLI

Once Exoframe server is up, you’ll want to install Exoframe CLI to communicate with it.
You have two options for installing the CLI.

  1. If you already have Node.js and npm installed on your system, you can install it via npm: npm install exoframe -g
  2. If you don't have Node.js and prefer not to install it, you can download a pre-built binary from releases on GitHub.

Step 3: Hooking up CLI to your server

After the setup you will need to point CLI to your server using the following command:

$ exoframe endpoint https://exoframe.server.url

And finally login into it by using exoframe login, selecting the private key that has been authorized to access your server and entering a passphrase if needed.

That's it, you are ready to deploy!

Deploying with Exoframe

To deploy a project, simply execute exoframe from the project folder.
Configuration for deployment is done using config file that contains the name of your project, domain you want to assign to it, environment you want to set, restart policy and a bunch of other options. Structure of config file is covered in Exoframe docs.
You can also use exoframe config command to interactively generate or edit config file for current project.

The deploy command uses project templates to auto-generate Dockerfiles (if none a present). Exoframe currently comes bundled with the following project types:

  • Static HTML based projects - will be deployed using nginx image
  • Node.js based projects - will be deployed using node image
  • Docker-based projects - will be deployed using your Dockerfile
  • Docker-compose based projects - will be deployed using your docker-compose file

Additionally, you can install and use maven, java and tomcat templates.

You can also create your own templates as needed by following this guide.

Now let's look at some additional small features that Exoframe provides for your deployments.

HTTPS with Letsencrypt

Since Exoframe uses Traefik to manage domains, you get out-of-the-box support for HTTPS with letsencrypt certificates!
To enable it, you’ll need to edit your server config file and set the following fields:

letsencrypt: true # whether to enable letsencrypt, default "false"
letsencryptEmail: your@email.com # email used for letsencrypt

Once this has been set, just start your Exoframe server.

Warning: If you already had Exoframe server and Traefik running, you’ll also need to remove old Traefik instance from Docker daemon and restart Exoframe server. This is required since Traefik needs to be re-created with additional letsencrypt parameters.

Once this is done — all your projects will be automatically deployed to HTTPS with letsencrypt certificates!

Automatic subdomain generation

Exoframe also allows you to enable automatic subdomain names generation. This can be done by setting baseDomain field in server to config to the top domain you want to use.
E.g. setting it to .codezen.net will result in deployed services without explicitly specified domain names getting domains like exo-user-demo-d234ah3.codezen.net
This is useful when you want to quickly deploy and test your projects without using the real domain name for them.

Continuous deployment with Exoframe

Exoframe has a special feature called “deployment tokens” that allows you to deploy projects to your server without the need to login using your private key. To setup this deployment process, you first need to generate such token. This can be done by executing the following command:

$ exoframe token

Upon execution you should get a freshly generated token that can be used to deploy projects to the current Exoframe server.

Important note: Make sure to save the token as there’s no way to read it again, only to generate a new one.

Once you have the token, you can use it in your CI service to deploy your projects using Exoframe by passing it during deploy, like so:

$ exoframe -u -t $YOUR_EXOFRAME_TOKEN

You can find more detailed guide on setting it up with Travis-CI here.

Deployment recipes

Docker allows to deploy complex systems using docker-compose files, but such deployments are limited due to Docker constraints - you cannot wait for services to become available, cannot execute post-init scripts, it might be hard to prepare configs, etc.
There are of course workarounds for some of those things - some of them are mentioned in the official Docker docs, others discussed in github issues.

Exoframe aims to address some those problems with “recipes” feature. It provides a way to run third-party complex deployment recipes (that are essentially javascript code that has direct access to Docker daemon).
Those recipes provide a quick and easy way to deploy complex projects in one command.

The way recipe work is pretty straightforward:

  1. Developer writes an exoframe recipe and publishes it on npm
  2. User installs and configures Exoframe on server and locally
  3. User executes recipe with exoframe setup recipe-name where recipe-name is the package name from npm
  4. Exoframe pulls the recipe from npm and presents a set of developer-defined questions to the user (see screenshot below)
  5. Once the user answers the questions, Exoframe server executes the recipe code and sets up the system in a required way

As an example recipe, you can deploy Wordpress backed by MariaDB along with PHPMyAdmin by simply executing exoframe setup exoframe-recipe-mysql and answering a set of simple questions.
Or you can deploy your own Ghost blog by simply executing exoframe setup exoframe-recipe-ghost.
If you are interested in more complex recipe examples - check out this one for a big data benchmarking platform - HOBBIT project.
Pretty neat, ain't it? 😁

Current limitations

There are two current limitations related to Exoframe interactions with Docker:

  1. Limited support for docker-compose projects configuration. While Exoframe can easily manipulate containers it deploys directly, changing configuration of docker-compose services is quite limited. Because of that, you’ll have to set e.g. Traefik labels for domains manually. Unfortunately, there’s no real way to “fix” this issue but to provide better docs.
  2. Limited swarm support. While current version of Exoframe does work in swarm mode, if you try deploying a project from source files (i.e. building the image) - the deployment into a cluster will fail. This is due to a fact that Exoframe doesn't have any way of syncing built image across swarm nodes. There's a plan to fix this shortcoming eventually though.


If you found Exoframe interesting - give it a shot!
And as usual - any feedback as well as contributions are appreciated 😄

Posted on by:

yamalight profile

Tim Ermilov


Hi, I'm Tim. I talk about webdev, javascript and video games.


markdown guide