A self-hosted deployment controller for anything
Runway is deployment controller that runs on an event driven system. You define the events that should trigger deployments and then you configure how you want those deployments to be executed. Runway is not a CI/CD system, it is a deployment controller. It is meant to be run on a server that can reach the internet and can also reach your target servers or projects. It can run on the same server as your projects, or on a separate server. It is up to you how you want to configure it.
See the full project goals here for even more information about why this project was created.
- 🔍 Event driven system that looks for deployment events that you configure
- ✏️ Configurable - You define the events, how often runway should check for events, and how deployments should be executed
- 📦 Plugable - You can write new deployment strategies or deployment events to extend runway
- 🦾 ARM Support - Runway's pre-built Docker images run on both
x86_64
platforms andARM
platforms - 🚀 Native github/branch-deploy support - Runway can look for, and complete GitHub deployments
- 🐳 Fully Dockerized - Runway has pre-built Docker images that make it easy to get started
- 🌱 Small Footprint - Runway is written in crystal and has a tiny memory footprint. It can even run on a Raspberry Pi 4!
This section goes into a brief example of how you can use runway
Here is a very basic example of using runway:
First, create a runway configuration file (config.yml
). This config file tells runway how to run.
# This is a runway configuration file
# config.yml
projects:
- name: project-1
deployment: # deployments are actions that get triggered by events
type: command # this deployment type is a command that gets run
location: local # the command type is 'local', so the command is executed on the machine by runway
path: /home/bob/project-1/ # go to this path before running the command
timeout: 5
entrypoint: bash
cmd: ["-c", "echo 'I did a cool deployment!'"] # add your own custom logic here to deploy project-1
events: # events are actions that can trigger deployments
- type: file
path: deploy-it.txt # look for this file and if it's found, trigger the deployment
cleanup: true # remove the file after the event
schedule:
interval: 3s # how often to check for the event
Now start runway:
runway -c config.yml
In this example, runway will check for the existence of the deploy-it.txt
file in the current directory every 3 seconds. If this file is found, that is an event trigger. Event triggers kick off the logic defined under the deployment
section for a given project.
So if we were to create the deploy-it.txt
file, runway would execute the command
deployment. The command
deployment would go to the /home/bob/project-1/
directory on the local
system and run bash -c echo 'I did a cool deployment!'
. After the deployment is complete, the deploy-it.txt
file gets cleaned up.
You can go ahead and test out this example for yourself to give runway a go!
Here is a more complex example of using runway:
First, create a docker-compose.yml
file which will be responsible for starting runway and mounting a volume that contains your runway configuratin file:
# example docker-compose.yml file
services:
runway:
container_name: runway
restart: unless-stopped
image: ghcr.io/runwaylab/runway:vX.X.X # <--- replace with the tag you want to use
command: ["-c", "config/config.yml"]
volumes:
- keys:/app/keys
- config:/app/config
env_file:
- creds.env
volumes:
config:
driver: local
driver_opts:
o: bind
type: none
device: ./config
keys:
driver: local
driver_opts:
o: bind
type: none
device: ./keys
Next, create a runway configuration file at config/config.yml
relative to your docker-compose.yml
file. This directory gets mounted into runway's container so that your config is accessible to runway.
# This is a runway configuration file
# config/config.yml
projects:
- name: project-1
deployment:
type: command # this deployment type will run a command
location: remote # the location will be "remote" so runway will attempt an SSH connection to the remote server
remote:
auth: publickey # the auth mode will be via a public/private key pair to the remote server (SSH)
host: "192.168.1.5" # this is the host to SSH into - perhaps another host on your home network
port: 22 # the port to use for SSH connections
username: ubuntu # the username of the system you want to SSH to
public_key_path: /app/keys/id_rsa.pub # the public key from the "keys" volume to use for public key auth
private_key_path: /app/keys/id_rsa # the corresponding private key of the public/private key pair
success_string: deployment-complete # the string to look for in the command's output to determine if the deployment was successful
timeout: 30 # the total SSH / command timeout for execution
entrypoint: bash # the entrypoint of the command to run on the remote host
cmd: ["-c", "'script/deploy --ref={{ payload.ref }} && echo deployment-complete'"] # the actual command arguments to run
events: # events that runway looks for to trigger a deployment
- type: github_deployment # github deployment event
repo: runwaylab/test-flight # the repository to check for deployment events
environment: production # the specific environment to check for
deployment_filter: 1 # only look at the most recent deployment (based on the created_at field) - this field is required only for the github_deployment type event. It helps to save on API requests to GitHub. If not provided, it defaults to 1
schedule:
interval: 15s # intervals can be in milliseconds, seconds or minutes (ms, s, or m) - or a cron expression
timezone: UTC # the timezone to use for the schedule (default is UTC) - ex: Europe/Berlin or America/New_York - see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
- type: github_deployment # here we define another event to look for, in this case we also check the staging environment
repo: runwaylab/test-flight
environment: staging
deployment_filter: 1
schedule:
interval: 5s
timezone: UTC
Now we need to create a keys/
directory that contains our public/private key pair that we need for runway to SSH into the remote host we defined above.
Please ensure the
keys/
dir has 700 permissions and your public/private keys have 600 permissions
We also need to create a creds.env
file which contains a GitHub PAT that is scoped to allow read
access to deployments
since our configuration is specifically looking for GitHub deployments. You may also need repo
permissions on the token as well if your repository is private.
# creds.env
GITHUB_TOKEN=ghp_abcdefg
Now we can fire up runway!
docker compose up --build -d
Let's explain what this all did:
- We created a docker compose service to start runway
- We configured our docker compose service to mount a
keys
and aconfig
volume from our local disk. Thekeys
volume contains the public/private key pair used for remote SSH commands on a given server with public key authentication - We created a new runway configuration file (under
config/config.yml
) giving runway events to listen for (in_progress
GitHub deployments) and deployments to run when these events are triggered - We created a new
keys/
directory and placed our public/private keys inside of it (with the correct permisions) - We created a new
creds.env
file containing ourGITHUB_TOKEN
so that runway can authenticate and listen for / complete GitHub deployments
Now if runway detects an in_progress
deployment for the runwaylab/test-flight
repository under either the production
or staging
environment, it will SSH into the remote
server and execute the script/deploy
script with the provided GitHub REF that triggered the deployment.
Note: Yes this example was complex and verbose. Yes this example requires some fine tuning and setup to work for your project... but that is the point, showing you what can be accomplished with runway and how you can leverage it for your own projects in a very flexible/open way!
Using the github_deployment
deployment type makes runway the ultimate deployer of your application as far as GitHub deployments are concerned. In order to fully leverage this deployment type, you need to create deployments, and not complete them within GitHub Actions. This is because runway will look for them, run its defined deployment configuration, and then complete them for you!
To see a live example of how this works using github/branch-deploy and Actions, checkout this live example here
There are a few ways to go about installing and using runway:
Docker is the suggested way to run runway. You can pull the latest runway image from GitHub Container Registry. Images are built with amd64
, and arm64
support. You can run runway out of the box with Docker on Linux x86_64 platforms and ARM64 platforms (like a Raspberry Pi).
You can download pre-built binaries from the releases page for your desired platform.
For extra security, you can verify each release binary with the GitHub CLI. Here is an example:
$ gh attestation verify runway-linux-x86_64 --owner runwaylab
Loaded digest sha256:2bd82ad62195a6b20c491585d0ff7477dd0189a08469f53ba73546807596957f for file://runway-linux-x86_64
Loaded 1 attestation from GitHub API
✓ Verification succeeded!
You can also build runway from source. To do this, you will need to have crystal installed. Make sure you are using the same version this project uses by checking the .crystal-version
file.
Check out the CONTRIBUTING.md file to make sure you have the necessary dependencies installed (you probably already have them, but if not it only takes a few commands to install them).
First, run script/bootstrap
to install all crystal dependencies. Then, you can run script/build
to build the runway binary. The binary will be placed in the bin/
directory and it will be named runway
.
Events are "things" that runway looks for which trigger deployments. Events can be anything, from the existence of a new file, to a new tag being published, to a GitHub deployment.
For a complete list of event types and how to use them, see the events documentation.
Deployments are triggered by events and define how your "project" gets deployed. Deployments are generally commands that runway executes.
For a complete list of deployment types and how to use them, see the deployments documentation.
See the contributing documentation to learn more about how you can contribute or develop runway.