Skip to content

Repository files navigation

Outpost

Outpost — remote power, local control

Outpost turns a remote Linux host into a shared development environment you control from your local terminal. Run Docker Compose stacks, Kubernetes clusters, and lightweight Linux machines on the server — without installing Docker, kubectl, or a local VM stack on your laptop.

Use an existing Linux server or let Outpost provision one on AWS. Share the host with teammates through invitation codes; collaborators get runtime access without your cloud credentials.

What you get

  • Remote Docker and Compose — develop against containers on a shared host, not your laptop.
  • Kubernetes with kind — create named clusters and run kubectl remotely.
  • Linux machines with Incus — system containers by default; full VMs when the host supports KVM.
  • Local port forwarding — reach remote services at http://127.0.0.1:8080 from your machine.
  • Remote mirror — sync your repo and run commands on the host; detached tmux sessions survive disconnects.
  • Team sharing — invite collaborators with device approval; owners keep control of the host and cloud account.

How it works

You install the Outpost CLI locally. It connects to your host over SSH, installs missing tools on first use, syncs project files, and runs commands remotely. There is no Outpost agent running on the server.

Your machine                Remote Linux host
─────────────               ─────────────────
outpost CLI        SSH  →   Docker + Compose
~/.outpost/ (global)        kind + kubectl
.outpost/ (per repo)        Incus

When you run outpost compose up, Outpost uploads your compose files (and .env if present) to the host and starts the stack there. When you run outpost connect, it forwards published ports to your localhost.

Install

Install script (macOS and Linux):

curl -fsSL https://raw.githubusercontent.com/degoke/outpost/main/scripts/install.sh | bash

Pin a version:

curl -fsSL https://raw.githubusercontent.com/degoke/outpost/main/scripts/install.sh | OUTPOST_VERSION=v0.1.0 bash

The script installs to ~/.local/bin by default. Override with OUTPOST_INSTALL_DIR.

From source (requires Go 1.26+):

go install github.com/degoke/outpost/cmd/outpost@latest

Manual download — binaries for each platform are on GitHub Releases.

Requirements

Your machine Outpost CLI, SSH client, network access to the host
Remote host Linux with SSH; sudo for first-time setup
Supported distros Debian/Ubuntu, Amazon Linux, RHEL, CentOS, Rocky, and similar
AWS (optional) Configured AWS CLI profile with EC2 permissions

Getting started

Use an existing server

# 1. Register the host (verifies SSH and bootstraps Docker)
# Password-only VPS (default when no --identity-file is given):
outpost host add personal --hostname 203.0.113.10 --user ubuntu --auth password

# Or with a dedicated key file:
outpost host add personal --hostname 203.0.113.10 --user ubuntu --auth key --identity-file ~/.ssh/vps_key

# 2. Re-verify later if needed
outpost host verify

# 3. Initialize your project (in a repo with docker-compose.yml)
outpost init

# 4. Start your stack
outpost compose up -d

# 5. Forward ports to your machine
outpost connect

Your services are now available on localhost — for example http://127.0.0.1:8080 if that port is published in compose.

Provision a host on AWS

outpost provider login aws --profile my-profile --region eu-west-1
outpost host create personal --provider aws --region eu-west-1
outpost host verify

outpost init
outpost compose up -d
outpost connect

Outpost creates the EC2 instance, configures SSH, installs Docker, and registers the host. You can start, stop, resize, or destroy it with outpost host commands.

Day-to-day usage

Hosts

outpost host list                    # list registered hosts
outpost host use personal            # switch active host
outpost host verify                  # check connection and dependencies
outpost host capabilities            # see what the host supports (e.g. VMs)

Use --host NAME on any command to target a specific host without changing the active one.

Projects and Compose

In each repository, run outpost init once. It creates a .outpost/ directory with your project configuration.

The .outpost/ folder (in your repo)

When you run outpost init, Outpost creates a .outpost/ directory at the root of your repository. This is local project metadata — it tells the CLI how to map your repo to a remote workspace. It is never synced to the remote host (Outpost always excludes .outpost/ from mirror uploads).

File Purpose
project.yaml Stable project name, optional host override, compose file list, and remote directory path
.outpostignore Patterns for files/folders to exclude from mirror sync (same syntax as .gitignore)

Should you commit it? By default, yes — commit .outpost/ so teammates use the same project name and land in the same remote directory. If you prefer per-developer settings, run outpost init --write-gitignore to keep .outpost/ out of git.

my-repo/
├── .outpost/
│   ├── project.yaml      # shared project config (usually committed)
│   └── .outpostignore    # mirror sync exclusions (edit as needed)
├── docker-compose.yml
└── src/

This is separate from ~/.outpost/ on your machine, which stores global CLI state (registered hosts, SSH keys, port-forward sessions). See Configuration below.

outpost init --name my-api           # set a stable name (defaults to repo folder name)
outpost init --write-gitignore       # keep .outpost/ local instead of committing it

outpost compose up -d
outpost compose ps
outpost compose logs -f
outpost compose exec api sh
outpost compose down

outpost docker ps
outpost docker logs my-container

compose up, build, and pull sync your compose files to the host before running. Keep secrets in .env and out of version control — Outpost syncs .env to the host when it exists locally.

Create .outpost/.outpostignore (created automatically by outpost init) to exclude paths from mirror sync. Same syntax as .gitignore. In git repositories, it applies in addition to .gitignore:

# .outpost/.outpostignore
node_modules/
.venv/
dist/
*.log

Built-in excludes always apply: .git/, .outpost/, .DS_Store.

Remote mirror

Run scripts and commands on the host from your local repo without copying large generated outputs back to your laptop. Mirror syncs your repository (respecting .gitignore and .outpost/.outpostignore), runs commands in the project's remote directory, and supports detached tmux sessions that survive disconnects.

outpost mirror sync
outpost mirror sync --rsync              # faster incremental sync (requires rsync on both sides)
outpost mirror sync --workers 8          # parallel SFTP uploads (default: 6)

outpost mirror watch                     # continuously sync changes (Ctrl+C to stop)
outpost mirror watch --rsync             # rsync on each debounced change
outpost mirror watch --debounce 500ms

outpost mirror run node scripts/generate.js
outpost mirror run --sync -- npm test          # force sync even if nothing changed locally
outpost mirror run --no-sync -- python script.py # never sync
outpost mirror run -d --name gen node scripts/generate-40k.js

outpost mirror sessions list
outpost mirror sessions status gen
outpost mirror sessions attach gen
outpost mirror sessions kill gen

# Python (remote-only .venv — never synced from your laptop)
outpost mirror setup-python
outpost mirror setup-python --rsync
outpost mirror run python scripts/train.py
outpost mirror shell

Automatic sync skipping: mirror run and compose up skip syncing when local files have not changed since the last successful sync, or when mirror watch is already running in another terminal. Use mirror run --sync to force a sync.

For script-only repositories without Docker Compose, initialize with outpost init --no-compose.

Moving compose volumes between hosts

Named Docker volumes (for example Postgres data) stay on the host they were created on. Outpost can archive them locally and restore them on another host.

# On the old host: save volumes to ~/.outpost/archives/{project}/
outpost compose volumes export

# On the new host: restore from local archives
outpost compose volumes import

# Check status
outpost compose volumes list

When you run outpost compose up, Outpost automatically offers to import missing or empty volumes that have local archives. Use --yes to skip the prompt.

To move a project:

outpost host use old-host
outpost compose volumes export

# point the project at the new host in .outpost/project.yaml, then:
outpost host use new-host
outpost compose volumes import
outpost compose up -d

Port forwarding

outpost connect                      # forward all published compose ports
outpost connect --service api        # one service only
outpost connect --port 9090:80       # custom mapping
outpost connect --status             # show active sessions
outpost connect --down               # stop forwarding

Port already in use? Stop the local process on that port, or override with --local-port 18080 or --port 9090:80.

Sharing with your team

The host owner creates invitations; teammates join with a code and wait for approval.

# Owner
outpost invite create
outpost invite list
outpost invite approve DEVICE_ID
outpost invite revoke DEVICE_ID

# Teammate
outpost invite join CODE --hostname 203.0.113.10 --user ubuntu --label my-laptop

Members can run workloads (docker, compose, connect, kubectl, etc.) but cannot create or destroy hosts, manage invitations, or use cloud provider commands. Destructive operations warn when other teammates may be affected.

Members can Members cannot
docker, compose, connect Manage hosts or invitations
status, top, capacity, disk, prune init, host create/destroy, provider login
cluster list, kubectl cluster create/delete
machine shell, machine exec, machine copy, machine connect machine create/delete

AWS host management

outpost host create personal --provider aws --region eu-west-1
outpost host stop personal            # stop EC2 instance, pause compute billing
outpost host start personal           # start again and wait for SSH
outpost host restart personal
outpost host resize personal --instance-type t3.large
outpost host remove personal           # remove from local config only
outpost host destroy personal          # terminate the EC2 instance

stop pauses the instance without deleting it — you avoid EC2 compute charges while it is stopped. Attached EBS volumes (and Elastic IPs) may still bill. start brings the host back and waits for SSH.

host remove only forgets the host in your local config — the server keeps running. host destroy terminates the cloud instance.

Kubernetes

Create and use kind clusters on the host. No local kubectl required.

outpost cluster create dev
outpost cluster create staging --workers 2
outpost cluster list
outpost cluster status dev
outpost kubectl --cluster dev get nodes
outpost kubectl --cluster dev apply -f ./manifest.yaml
outpost cluster delete dev

Local manifest files are uploaded automatically when you apply them.

Linux machines

System containers are lightweight and work on most hosts, including standard EC2 instances. Defaults are minimal — sized for quick test environments on small VPS plans. Increase resources with --cpu, --memory, and --disk when you need more:

Resource Container default VM default
CPU 0.5 core 1 core
Memory 128 MiB 256 MiB
Disk 2 GiB 3 GiB

Containers vs VMs: A system container shares the host Linux kernel (like a very isolated chroot). It starts fast, uses little RAM, and works on almost any Linux host — this is the default. A VM runs a full guest kernel via KVM with stronger isolation, but needs more resources and only works when the host has KVM (bare metal, metal EC2, or nested virtualization). Use containers for everyday dev/test; use VMs when you need a real kernel or kernel modules.

Outpost checks host capacity before creating a machine. If the host is low on resources, the command fails with available amounts — run outpost capacity to inspect the host, or request a smaller machine.

outpost machine create ubuntu-dev --image ubuntu:24.04
outpost machine create big-dev --image ubuntu:24.04 --cpu 2 --memory 2GiB --disk 20GiB
outpost machine shell ubuntu-dev
outpost machine exec ubuntu-dev -- uname -a
outpost machine copy ./app ubuntu-dev:/tmp/app
outpost machine copy ubuntu-dev:/tmp/output.log ./output.log
outpost machine connect ubuntu-dev --port 8080:80
outpost machine stop ubuntu-dev
outpost machine snapshot create ubuntu-dev
outpost machine delete ubuntu-dev

Virtual machines need KVM. They work on bare-metal servers, metal EC2 instance types, or hosts with nested virtualization. Standard t3.* instances do not support VMs — use system containers instead. VMs typically need more memory than the default; set --memory explicitly.

outpost host capabilities
outpost machine create vm-dev --image ubuntu:24.04 --virtual-machine --cpu 2 --memory 2GiB --disk 20GiB

Monitoring and cleanup

outpost status          # host health and workload summary
outpost top             # live container CPU and memory
outpost top --watch
outpost capacity        # free resources and recommendations
outpost disk            # disk usage and reclaimable space

outpost prune --dry-run # preview cleanup
outpost prune           # remove stopped containers, unused images, build cache
outpost prune volumes   # explicit: unused named volumes

Configuration

Outpost stores configuration in two places:

Location Scope Purpose
~/.outpost/ Your machine (global) Registered hosts, SSH keys, active host, port-forward sessions, volume archives
.outpost/ Each repository (local) Project name, host override, compose files, mirror sync ignore rules

Global config (~/.outpost/)

Created automatically on first use. You normally do not edit these by hand.

File / directory Purpose
config.yaml Registered hosts, active host, AWS defaults
identities/ SSH keys generated for cloud hosts
sessions/ Active port-forward session metadata
archives/ Exported Docker volume backups
sync-state/ Local fingerprints used to skip redundant syncs

Project config (.outpost/ in your repo)

Created by outpost init. Not uploaded to the remote host.

File Purpose
project.yaml Per-repo project name, host, and compose files
.outpostignore Extra ignore rules for mirror sync / mirror watch

Example project config (created by outpost init):

name: my-api
host: personal
remote_dir: /var/lib/outpost/projects/my-api
compose_files:
  - docker-compose.yml

Use the same project name across your team so everyone targets the same remote stack.

Command-line options

These flags work on every command:

Flag Description
--host NAME Use a specific host instead of the active one
--json JSON output
--debug Verbose logging
--yes Skip confirmation prompts

Troubleshooting

Problem What to try
SSH connection fails Test with ssh user@host. Check hostname, user, port, and key. Pass --identity-file to host add if needed.
Bootstrap fails Ensure your user has sudo on the host. On unsupported distros, install Docker manually, then run outpost host verify.
Port forwarding conflict Run outpost connect --status. Use --local-port or --port to pick a different local port.
Member access denied Owner runs outpost invite list and approves the device.
Not enough resources Run outpost capacity before creating stacks, clusters, or machines.
Start over locally Run outpost reset to clear ~/.outpost (hosts, keys, sessions). Remote servers and repo project files are kept.

License

Outpost is open source software licensed under the MIT License.

About

CLI for remote Linux dev: sync your repo, run commands, Docker Compose, Kubernetes, Linux machines, port forwarding, and team sharing.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages