Skip to main content

v0.20.0

This page mirrors the GitHub release, which also includes the full list of commits.

This release adds secrets for passing sensitive data to services, a new uc proxy command for port-forwarding, a Prometheus metrics endpoint, uc machine ls that surfaces each machine's OS, Docker, and daemon versions. Plus daemon self-upgrade on uc machine init/add, WireGuard MTU auto-detection, shell completion, and more.

⚠️ This is a breaking release. Both the uc CLI and the uncloudd daemon on every machine must be upgraded to 0.20. See Breaking changes below before you upgrade.

⚠️ Breaking changes​

0.20 changes the internal gRPC API proxy format and migrates the cluster store to a new Corrosion version, so a mixed 0.19/0.20 cluster will not work. The built-in version check rejects requests between incompatible CLI and daemon versions, so upgrade everything together. See Upgrade to 0.20.0 for instructions.

  • Server-side gRPC proxy resolution (#247). API proxy targets (machines) are now resolved on the daemon instead of the CLI. This changes the request format on the wire, so old and new components can't talk to each other. Thanks to @jabr for the contribution ❤️
  • Corrosion now runs as a managed container (71da325). The Corrosion cluster store moved from a systemd unit to a Docker container and was upgraded to Corrosion v1 (2026.6.15). Existing data is migrated to the new format automatically on first daemon start. Auth is now enabled for local Corrosion API requests too (#110).
  • CLI artifact renamed uncloud_* → uc_* (a5d73fe). Homebrew and the install script handle this for you. Only update your own scripts if you download the CLI archive directly. The daemon archive name (uncloudd_*) is unchanged.
  • Unregistry image proxy moved from port 5000 to 51500 (d36ff67). uc handles this automatically when pushing images. Only update your setup if you reach this proxy port directly in your own tooling.

Secrets support​

PR: #403, docs: e01c8d3

You can now keep sensitive data like passwords, API tokens, and private keys out of your Compose file. Define a secret with the new x-command extension and reference it from a service's environment with secret://<name>. The command runs locally at deploy time and pulls the value from your password manager, cloud secrets store, or any CLI you already use.

compose.yaml
services:
api:
image: myapp:latest
environment:
DB_PASSWORD: secret://db_password

secrets:
db_password:
x-command: op read "op://prod/myapp/db_password"

The value never lands in your Compose file or git. See the new Secrets doc for supported sources, caveats, and examples.

Proxy a service port locally​

PR: #384. Thanks to @miekg for the contribution ❤️

The new uc proxy command forwards a service port to a local port over the cluster network, similar to kubectl port-forward. This is handy for reaching an internal database or admin UI from your laptop without exposing them publicly.

Richer uc machine ls​

Changes: 8aa0a96, ad43569, 1d5c717, 23d6b44

uc machine ls now shows the OS, KERNEL, ARCH, DOCKER, and VERSION (daemon) of each machine. You can spot version drift and outdated daemons at a glance.

$ uc machine ls
NAME STATE ADDRESS PUBLIC IP WIREGUARD ENDPOINTS OS KERNEL ARCH DOCKER VERSION
machine-1 Up 10.210.0.1/24 203.0.113.10 203.0.113.10:51820 Debian 12.12 6.1.0-41-amd64 amd64 29.4.0 0.20.0
machine-2 Up 10.210.1.1/24 198.51.100.7 198.51.100.7:51820 Debian 12.5 6.1.0-18-amd64 amd64 26.0.0 0.20.0
machine-3 Up 10.210.2.1/24 - 192.0.2.44:51820 Ubuntu 22.04.5 LTS 6.8.0-1050-oracle arm64 29.5.3 0.20.0

Pass -o json for machine-readable output to use in scripts.

Prometheus metrics​

PR: #304. Thanks to @miekg for the contribution ❤️

Each daemon now serves the initial set of Prometheus metrics on http://<machine-ip>:51090/metrics, listening on the machine's internal IP. Point your scraper at it to monitor the cluster.

Daemon self-upgrade on machine init/add​

Change: a26af3b

uc machine init and uc machine add now upgrade the installed daemon binary to the latest (or a specified --version) if the daemon was already installed on the machine.

Improvements​

  • WireGuard now auto-detects the optimal interface MTU, with a new --wg-mtu flag to override it (0c3b8b1).
  • New --wg-port flag for uc machine init/add to customise the WireGuard listen port (#366). Thanks to @dasunsrule32 for the contribution ❤️
  • x-ports can bind to multiple host IP addresses given as a CIDR prefix (#358). Thanks to @miekg for the contribution ❤️
  • New uc version and uncloudd version commands that print full build information, replacing the --version flag (2f15915).
  • uc machine init/add installs curl on the remote machine if it's missing (#402).
  • New uc caddy logs alias for uc logs caddy (#381).
  • Shell/argument completion for the uc and ucind CLIs (machines, services, volumes) (#309, #344, #352). Thanks to @miekg for the contributions ❤️
  • Support rootless Docker locally when pushing images to the cluster (#347). Thanks to @tonyo for the contribution ❤️
  • The CLI strips ANSI colors from table output and skips interactive TUI elements when stdout is not a TTY (e3823c2, #386).

Bug fixes​

  • Reject relative volume source paths in Compose with a clear error (#353). Thanks to @miekg for the contribution ❤️
  • Return a non-zero exit code when a uc command is cancelled by declining a confirmation prompt (#354).
  • Don't include outdated containers from a removed machine in the DNS and Caddy configs (07acb31).
  • Improve context cancellation and Corrosion resubscription handling during watch and sync operations (328d175, 83256b5, 99e1ae0, 5f07bc5).
  • Use a forked Bubble Tea that drains pending TTY input on shutdown (#286).

Upgrade to 0.20.0​

Upgrade all machines and your local CLI to 0.20. Corrosion data migrates automatically on the first daemon restart.

important

On the first restart, each daemon pulls the Corrosion container image ghcr.io/unlabs-dev/corrosion:2026.6.15. Make sure your machines can reach ghcr.io, or pre-pull the image, otherwise the cluster store won't start.

Uncloud CLI locally​

# Homebrew (macOS, Linux)
brew upgrade uncloud

# Install script (macOS, Linux)
curl -fsS https://get.uncloud.run/install.sh | sh

Machine daemon​

Run the following commands on each machine:

ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -fsSL -o uncloudd.tar.gz https://github.com/psviderski/uncloud/releases/download/v0.20.0/uncloudd_linux_${ARCH}.tar.gz
tar -xf uncloudd.tar.gz
sudo install uncloudd /usr/local/bin/uncloudd
rm uncloudd uncloudd.tar.gz
sudo systemctl restart uncloud

After upgrading​

On the first restart, the daemon migrates the Corrosion store and stops the legacy systemd service. A couple of leftovers need manual cleanup on each machine:

  • The old store is preserved at /var/lib/uncloud/corrosion.backup-<timestamp>. Once you've verified the cluster is healthy (uc machine ls), you can remove it:

    sudo rm -rf /var/lib/uncloud/corrosion.backup-*
  • The legacy Corrosion systemd unit and binary are left in place because the daemon's sandbox doesn't have permission to delete them. Remove them manually:

    sudo rm /etc/systemd/system/uncloud-corrosion.service /usr/local/bin/uncloud-corrosion
    sudo systemctl daemon-reload