@@ 1,9 1,16 @@
Up is a single command to get your servers are up-and-running. You can think of
`up` as a partial replacement for Kubernetes, Nomad, Docker Swarm, and other
deployment tools. Unlike those other tools, `up` is extremely small, simple,
and as a result, more reliable and less prone to bugs.
up(1) is a batching, parallel task-runner.
It's designed primarily to deploy servers, where you can think of up(1) as a
partial replacement for Kubernetes, Nomad, Docker Swarm, and other deployment
tools. Unlike those other tools, `up` is extremely small, simple, and as a
result, more reliable and less prone to bugs.
up(1) is deliberately similar to make(1) in its config syntax. It differs
primarily in its ability to run tasks in batches and prompt before continuing,
a small but important difference which enables up(1) to handle zero-downtime,
blue-green, rolling deploys.
@@ 13,12 20,10 @@ $ go get -u egt.run/up/cmd/up
Up extracts the logic of deployment best-practices into a cross-platform tool
that can be used to deploy anything.
Like make(1), up(1) can be used to deploy anything.
You'll describe your server architecture in a single file (`Upfile`), then
use the `up` command to bring everything online. The syntax of the Upfile is
deliberately similar to Makefiles.
You'll describe your server architecture in a single file (`Upfile(5)`), then
use the `up` command to bring everything online.
Each Upfile contains one or more commands. All commands run locally, so remote
commands can be executed using something like `ssh user@$server "echo 'hi'"`
@@ 35,23 40,20 @@ executing commands on.
You can also use environment variables, like the following:
USER=dev up -c deploy -t production
USER=dev up -t 188.8.131.52 deploy
Access that variable in your Upfile using `$USER`.
You can access that variable in your Upfile using `$USER`.
Running commands on the remote host is as simple as using whatever shell you've
configured for your local system. See the below example Upfile designed for
bash, which runs remote commands using ssh:
# deploy is a command. Everything that follows on this line, similar to Make,
# is a requirement. In this example, running `up deploy` will first run
# check_health and check_version. If check_health or check_version fail (return
# a non-zero status code), then the commands are run. If both succeed, deploy
# is skipped on this server.
deploy check_health check_version
# your steps to compile and copy files to the remote server go here.
# Your steps to compile and copy files to the remote server go here.
# If any of the following lines have non-zero exits, up immediately
# exits with status code 1.
go build -o myserver git.sr.ht/~example/myserver
@@ 60,34 62,15 @@ deploy check_health check_version
ssh $remote 'sudo service myserver restart'
sleep 10 && $check_health
ssh $remote 'sudo apt -y update && sudo apt -y upgrade'
ssh $remote 'sudo snap refresh'
curl -s --max-time 1 $server/health
expr $CHECKSUM == `curl --max-time 1 $server/version`
An **inventory.json** file must also be defined:
"10.0.0.1": ["production", "debian"],
"10.0.0.2": ["production", "debian"],
"10.0.0.3": ["staging", "debian"]
Using the example Upfile above, here's how we could deploy to staging:
up -c deploy -t staging
up -t 184.108.40.206 deploy
Since `up` does these tasks by running arbitrary shell commands defined in your
@@ 102,18 85,42 @@ project-level `Upfile`, `up` works out-of-the-box with:
If we want to deploy to staging and production, we'd write:
up -c deploy -t staging,production
up -t 220.127.116.11,18.104.22.168 deploy
To update all of our debian servers, 2 at a time, and exit immediately if any
fail, we can run:
up -c update -t debian -n 2
up -t 22.214.171.124,126.96.36.199 -n 2 update
Run `up -h` for additional usage info.
### Composition with other tools
Often in systems you'll have services deployed to dynamic IP addresses, so you
won't want to hardcode IPs in your deployment scripts. You can use a service
discovery mechanism to pass IPs to up and dynamically look up services by name.
For instance, using [inv2ips](https://www.egt.run/inv2ips) (which outputs a
comma-separated list of IP addresses) with an inventory.json file:
"188.8.131.52": ["production", "app"],
"184.108.40.206": ["production", "database"],
"220.127.116.11": ["staging", "app"]
We can run our deploy task on all production servers like so:
up -t $(inv2ips production) deploy
# equivalent to: up -t 18.104.22.168,22.214.171.124 deploy
- [x] Define your system architecture in source control
@@ 1,20 0,0 @@
"Inventory": ["10.0.0.1", "10.0.0.2"],
"sudo systemctl restart \"my_app 2\"",
"sleep 5 && $check_health"
"Rsync": "rsync -chazP -e 'ssh -J jump@$jump'",
"SSH": "ssh -J jump@$jump",