Wc
Observability AWS

WUD Container Update Monitor 9.6.0 on AWS User Guide

| Product: WUD Container Update Monitor 9.6.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and verifying WUD Container Update Monitor 9.6.0 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Uptime Kuma, Gatus & Uptime and Status Pages AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other products in the listing are described on the listing's guide hub.

WUD watches the containers running on a Docker host and tells you when a newer image tag or a new digest of the same tag is published in their registries (Docker Hub, GitHub Container Registry, Amazon ECR, GitLab, Quay and many more, plus any registry you add). It shows every container, its current and newest version, and the history of what it found, and it can notify you through triggers: HTTP webhooks, email, Slack, Teams, Telegram, MQTT, ntfy, Gotify and others.

On this image WUD watches the Docker Engine on the same instance. So that your first sign-in already shows WUD at work, the image ships a small demonstration: a read-only local registry holding a cloudimg demonstration image in versions 1.0.0, 1.1.0 and 1.2.0, and a container started on first boot from the deliberately old 1.0.0. WUD finds 1.2.0, marks the update and sends a notification to a local webhook receiver, all without touching the internet. You can remove the demonstration whenever you like (Step 6).

The image runs WUD 9.6.0 built by cloudimg from the official 9.6.0 source, with six of its dependencies updated to releases that fix every known advisory (npm audit reports none), on Node.js 24.22.1, with Docker Engine 29.9.2 from Docker's own repository. The base is a fully patched Ubuntu 24.04 LTS, and unattended security upgrades stay on for your instance, including for Docker Engine.

What is on the image:

  • WUD 9.6.0 (wud.service) running as the unprivileged wud user on 127.0.0.1:3000 only
  • Docker Engine 29.9.2 and containerd 2.4.2 (docker.service), reachable only through its unix socket
  • nginx publishing WUD over HTTPS on port 443 with a certificate created on your instance's first boot; port 80 redirects to HTTPS
  • A WUD administrator, wudadmin, with a password unique to your instance, created on first boot
  • The demonstration: cloudimg-wud-registry.service (read-only registry on 127.0.0.1:5000), cloudimg-wud-sink.service (webhook receiver on 127.0.0.1:3001) and the cloudimg-demo-app container
  • wud-set-hostname, a helper to serve WUD under your own DNS name
  • The build record (BUILD-INFO.txt), the dependency lockfile, the npm audit and govulncheck results and the licences in /usr/share/doc/wud/

Default user: ubuntu · Architecture: x86_64 · Root volume: 8 GiB gp3

Security posture

WUD reads the Docker Engine of the instance, so anyone who can use it can see every container, image and label on the host. The image is built around that:

  • Every page and every API call needs a sign-in. nginx asks WUD to check the caller's identity before it forwards anything: the web interface, the REST API, the live event streams, the API documentation, the Prometheus /metrics endpoint and the registry webhook receiver all answer 401 without valid credentials. The only exception is a static /healthz probe for load balancers, which returns ok and never reaches WUD.
  • Per-instance secrets. The administrator password, the key WUD signs its session cookies with and the TLS key are generated on your instance's first boot. WUD stores the password only as a bcrypt hash; first boot hands it over once and removes the hand-over file. Your credentials are in /root/wud-credentials.txt (mode 0600, root only). No database, password, key or session ships in the image.
  • Fail closed. WUD will not start until first boot has created the administrator and the session key, and nginx will not start until first boot has finished. There is no anonymous mode and no first-visitor setup page.
  • Loopback only. WUD listens on 127.0.0.1. nginx on 443 is the only way in.
  • HTTPS on first boot. nginx serves WUD with a TLS key and self-signed certificate created on your instance (TLS 1.2 and 1.3 only). Port 80 only redirects to HTTPS. Session cookies are marked Secure and SameSite.
  • Fixed host name. WUD is served under a name taken from the instance metadata at first boot (or one you set), never from the request. Requests for any other host name are dropped, and cross-site form posts, WebSocket connections and event streams are refused by nginx.
  • Real client addresses. nginx passes the connecting address to WUD and ignores any X-Forwarded-For header a client sends.
  • The Docker API stays private. Docker Engine listens only on its unix socket (/var/run/docker.sock, mode 0660, group docker); nothing listens on the Docker TCP ports 2375 or 2376. The only member of the docker group is wud.
  • No container-changing triggers. WUD can be configured to update containers or run commands when it finds a new version. None of those triggers is configured on this image; the only trigger is the demonstration's notification webhook.
  • No metadata access. WUD contacts the registries you configure and the webhooks you add, so wud.service and nginx.service are blocked from reaching the instance metadata service (169.254.169.254 and fd00:ec2::254). The two demonstration services can reach nothing but loopback. Amazon Time Sync keeps working.
  • Least privilege. WUD runs with no Linux capabilities, a read-only system (ProtectSystem=strict) except its own data directory, and private /tmp.
  • No scheduled firmware-metadata refresh. The fwupd firmware updater stays installed, but its LVFS metadata refresh (fwupd-refresh.timer and .service) is masked, so an LVFS outage never marks your instance unhealthy at boot; run sudo fwupdmgr refresh yourself if you use it.
  • IMDSv2 required. The AMI is registered with ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests.
  • Per-instance identity. SSH host keys, the machine ID and Docker Engine's engine ID are generated fresh on every instance.

Known advisories and how the build handles them

  • WUD dependencies. npm audit on the upstream 9.6.0 lockfile lists six advisories in WUD's production dependencies: body-parser (a denial of service on compressed request bodies), multer, undici, fast-uri, ip-address and nodemailer. This build updates each one within its own major release line to a release that fixes it (body-parser 1.20.9, nodemailer 10.2.1, multer 2.5.0, undici 8.12.3, fast-uri 3.1.9, ip-address 10.7.5); no other package version changes. npm audit on the result, and on the web interface's dependencies, reports no vulnerabilities. The results ship in /usr/share/doc/wud/. No vite (a development tool) is installed.
  • Test keys. Two dependencies publish their test files in their npm packages, including SSH and TLS test keys. Those test directories are removed from the image; a scan of the shipped files for private-key material finds none.
  • The web interface's icons. WUD's web interface loads some of its icons from the public Iconify icon service (api.iconify.design) in your browser. This is a request from the browser, not from the instance; if your browser cannot reach it, a few icons are blank.

Prerequisites

  1. An AWS account and permission to launch EC2 instances
  2. An EC2 key pair in the Region you launch in
  3. A subnet that gives the instance a public IPv4 address (or private connectivity from where you will use the web interface), and outbound HTTPS from the instance to the registries your images come from

Recommended instance type: m5.large. WUD is a single Node.js process with SQLite storage; the containers you run next to it decide how much more you need.

Connecting to your instance

Version Operating system SSH user
WUD Container Update Monitor 9.6.0 on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu

Step 1: Launch the instance

Option A: AWS Marketplace console

  1. Open the cloudimg Hardened Uptime Kuma, Gatus & Uptime and Status Pages AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  2. Once the subscription is active, choose Launch your software.
  3. Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose WUD Container Update Monitor 9.6.0 on Ubuntu 24.04, then choose your Region.
  4. Pick an instance type (m5.large recommended; the software charge is $0.04 per vCPU per hour) and your key pair.
  5. Security group: allow SSH (22), HTTP (80) and HTTPS (443). Restrict SSH to your own IP address and 80/443 to the people who use WUD: whoever signs in sees this host's containers.
  6. Choose your VPC and a subnet that gives the instance a public IP, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.

Option B: AWS CLI

Run this on your own workstation. It creates a security group that allows SSH and HTTPS from your management address only, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDR with your own:

SG=$(aws ec2 create-security-group --group-name wud \
  --description "WUD Container Update Monitor" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text)
for p in 22 80 443; do
  aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port "$p" --cidr <your-mgmt-cidr>
done
aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type m5.large \
  --key-name my-key-pair \
  --security-group-ids "$SG" \
  --subnet-id subnet-0123456789abcdef0 \
  --associate-public-ip-address \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=wud}]'

You do not need --metadata-options: the AMI already makes IMDSv2 required.

Step 2: Connect and read your credentials

Connect as ubuntu with the key pair you chose:

ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>

First boot finishes within a minute or two of launch. It creates the certificate, the session key and the administrator, starts Docker Engine and the demonstration, then lets nginx start. Your credentials are in a root-only file:

sudo cat /root/wud-credentials.txt
# WUD Container Update Monitor - generated on first boot by wud-firstboot.service
# These credentials are unique to this instance. Store them somewhere safe.
#
# "Docker" is a trademark of Docker, Inc. cloudimg is not affiliated with, endorsed by, or sponsored by Docker, Inc.

WUD_URL=https://ec2-34-239-126-168.compute-1.amazonaws.com/
wud.admin.user=wudadmin
wud.admin.pass=<WUD_ADMIN_PASSWORD>

# Open WUD_URL in a browser (self-signed certificate minted on this instance's first boot; port 80 redirects to
# https) and sign in with the user and password above. Every page and API call needs them.
# ANYONE WHO CAN SIGN IN SEES THIS HOST'S CONTAINER INVENTORY, and WUD reads the container engine as a member of
# the docker group: treat this login as privileged and restrict inbound 443 in your security group.
# Serve it under your own DNS name:  sudo wud-set-hostname <your-dns-name>
# Full deployment guide: https://www.cloudimg.co.uk/guides/wud-container-update-monitor-9-6-0-on-ubuntu-24-04-aws/

wud.admin.pass holds your instance's own password.

Step 3: Confirm the services are running

First boot, Docker Engine, the two demonstration services, WUD and nginx are active, WUD runs as the unprivileged wud user, and WUD reports healthy on its loopback port:

systemctl is-active wud-firstboot.service docker.service cloudimg-wud-registry.service cloudimg-wud-sink.service wud.service nginx.service
ps -o user=,args= -p "$(systemctl show -p MainPID --value wud.service)"
H=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/health); echo "WUD health (loopback): $H"
[ "$H" = 200 ] && [ "$(systemctl is-active wud.service nginx.service docker.service | sort -u)" = active ]
active
active
active
active
active
active
wud      /opt/node/bin/node /opt/wud/dist/index.js
WUD health (loopback): 200

These are the TCP ports the instance listens on:

sudo ss -Hltn | awk '{print $4}' | sort -u
0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
127.0.0.1:3000
127.0.0.1:3001
127.0.0.1:5000
127.0.0.1:8081
127.0.0.53%lo:53
127.0.0.54:53
[::]:22
[::]:443
[::]:80

WUD (3000), the demonstration webhook receiver (3001), the demonstration registry (5000) and the demonstration container (8081) listen on loopback only; nginx publishes WUD on 443. The loopback port 53 entries are Ubuntu's local stub resolver (systemd-resolved). Docker Engine has no TCP port at all.

The versions, the six updated dependencies and the build record:

jq -r '"WUD " + .version' /opt/wud/package.json
/opt/node/bin/node --version
sudo docker version --format 'Docker Engine {{.Server.Version}}'
for p in body-parser nodemailer multer undici fast-uri ip-address; do jq -r '"  \(.name) \(.version)"' "/opt/wud/node_modules/$p/package.json"; done
grep -E '^(source|runtime) ' /usr/share/doc/wud/BUILD-INFO.txt | cut -c1-110
jq -c '.metadata.vulnerabilities' /usr/share/doc/wud/npm-audit-app-prod.json
WUD 9.6.0
v24.22.1
Docker Engine 29.9.2
  body-parser 1.20.9
  nodemailer 10.2.1
  multer 2.5.0
  undici 8.12.3
  fast-uri 3.1.9
  ip-address 10.7.5
source          https://github.com/getwud/wud.git tag 9.6.0 = commit ae3c0827c020e62ba15b8df6edd5b04aa05e9e62
runtime         Node.js v24.22.1 (https://nodejs.org/dist/v24.22.1/node-v24.22.1-linux-x64.tar.xz, sha256 6c17
{"info":0,"low":0,"moderate":0,"high":0,"critical":0,"total":0}

Step 4: Sign in to WUD

Browse to the WUD_URL from Step 2, for example https://ec2-34-239-126-168.compute-1.amazonaws.com/. The certificate is self-signed, so your browser warns once; accept it, or install your own certificate (Step 7). Your browser then asks for a user name and password: enter wudadmin and the wud.admin.pass value. The Containers page lists what WUD watches. On a new instance that is the demonstration container, already flagged with an update from 1.0.0 to 1.2.0:

The WUD Containers page listing the cloudimg demo app watched by the local watcher, running version 1.0.0 with an available update to 1.2.0

You can confirm the sign-in rules from the command line. Port 80 only redirects to HTTPS, a request without credentials or with a wrong password is refused with 401, your credentials are accepted, and the only WUD user is your administrator. The block reads your password from the credentials file into a temporary file that curl reads, so it never appears on a command line, and stops with an error if any answer is not the expected one:

H=$(sudo sed -n 's/^set $wud_host "\(.*\)";$/\1/p' /etc/nginx/wud-host.conf)
R=(--cacert /etc/ssl/wud/wud.crt --resolve "$H:443:127.0.0.1" --resolve "$H:80:127.0.0.1")
K=$(mktemp); sudo awk -F= '/^wud.admin.user=/{u=$2} /^wud.admin.pass=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /root/wud-credentials.txt > "$K"
P80=$(curl -s "${R[@]}" -o /dev/null -w '%{http_code} %{redirect_url}' "http://$H/")
ANON=$(curl -s "${R[@]}" -o /dev/null -w '%{http_code}' "https://$H/api/containers")
WRONG=$(curl -s "${R[@]}" -u "wudadmin:not-the-password" -o /dev/null -w '%{http_code}' "https://$H/api/containers")
OK=$(curl -s "${R[@]}" -K "$K" -o /dev/null -w '%{http_code}' "https://$H/api/containers")
USERS=$(curl -s "${R[@]}" -K "$K" "https://$H/api/users" | jq -r '.[] | "\(.username) (\(.role))"'); rm -f "$K"
echo "port 80               : $P80"; echo "no credentials        : $ANON"; echo "wrong password        : $WRONG"
echo "your credentials      : $OK"; echo "WUD users             : $USERS"
[ "$P80" = "301 https://$H/" ] && [ "$ANON" = 401 ] && [ "$WRONG" = 401 ] && [ "$OK" = 200 ] && [ "$USERS" = "wudadmin (admin)" ]
port 80               : 301 https://ec2-34-239-126-168.compute-1.amazonaws.com/
no credentials        : 401
wrong password        : 401
your credentials      : 200
WUD users             : wudadmin (admin)

Scripts and monitoring tools can use the same credentials with HTTP Basic authentication, or a WUD API token you create under your profile in the web interface.

Step 5: See an update being detected

The demonstration container runs version 1.0.0 of the cloudimg demonstration image from the local registry, which also holds 1.1.0 and 1.2.0:

sudo docker ps --format '{{.Names}}  {{.Image}}  {{.Status}}'
curl -s http://127.0.0.1:5000/v2/cloudimg/demo-app/tags/list; echo
cloudimg-demo-app  127.0.0.1:5000/cloudimg/demo-app:1.0.0  Up 2 minutes
{"name":"cloudimg/demo-app","tags":["1.0.0","1.1.0","1.2.0"]}

WUD checks the registry for every container, reports the newest version, and sends a notification through its trigger. On this image the trigger posts to the local webhook receiver, which writes each delivery to /var/log/cloudimg-wud/trigger.log:

H=$(sudo sed -n 's/^set $wud_host "\(.*\)";$/\1/p' /etc/nginx/wud-host.conf)
R=(--cacert /etc/ssl/wud/wud.crt --resolve "$H:443:127.0.0.1")
K=$(mktemp); sudo awk -F= '/^wud.admin.user=/{u=$2} /^wud.admin.pass=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /root/wud-credentials.txt > "$K"
C=$(curl -s "${R[@]}" -K "$K" "https://$H/api/containers" | jq -r '.[] | "\(.name): running \(.image.tag.value), newest \(.result.tag), updateAvailable=\(.updateAvailable)"'); rm -f "$K"
echo "$C"
N=$(sudo grep -c '"updateAvailable":true' /var/log/cloudimg-wud/trigger.log); echo "notifications delivered to the demo webhook sink: $N"
[ "$C" = "cloudimg-demo-app: running 1.0.0, newest 1.2.0, updateAvailable=true" ] && [ "$N" -ge 1 ]
cloudimg-demo-app: running 1.0.0, newest 1.2.0, updateAvailable=true
notifications delivered to the demo webhook sink: 1

WUD checks every hour (WUD_WATCHER_LOCAL_CRON in /etc/wud/wud.env). To check now, choose Watch now on the Containers page.

Step 6: Watch your own containers

WUD watches every container on this instance's Docker Engine by default (WUD_WATCHER_LOCAL_WATCHBYDEFAULT=true). Run your containers as usual and they appear in WUD at the next check. Labels on a container tune what WUD compares, for example to consider only semantic version tags and show a friendly name:

sudo docker run -d --name web \
  --label 'wud.tag.include=^\d+\.\d+\.\d+$' \
  --label 'wud.display.name=Company website' \
  nginx:1.27.0

Public images on Docker Hub, GitHub Container Registry, Amazon ECR Public, Quay and the other providers listed on the Registries page are checked anonymously out of the box:

The WUD Registries page listing the anonymous public registry providers and the cloudimgdemo custom registry

For private registries, or to raise Docker Hub's anonymous rate limit, add credentials to /etc/wud/wud.env (it is readable by every local user, so keep secrets in a root-only file instead: see Maintenance), then restart WUD. For example, a private GitHub Container Registry:

WUD_REGISTRY_GHCR_PRIVATE_USERNAME=my-github-user
WUD_REGISTRY_GHCR_PRIVATE_TOKEN=<your-token>

Notifications are triggers. The image has one, the demonstration webhook:

The WUD Triggers page listing the single http trigger named cloudimgsink

Add your own in the same way, for example email or Slack:

WUD_TRIGGER_SMTP_MAIL_HOST=smtp.example.com
WUD_TRIGGER_SMTP_MAIL_PORT=465
WUD_TRIGGER_SMTP_MAIL_FROM=wud@example.com
WUD_TRIGGER_SMTP_MAIL_TO=ops@example.com
WUD_TRIGGER_SLACK_OPS_TOKEN=<your-token>
WUD_TRIGGER_SLACK_OPS_CHANNEL=#containers

WUD can also update containers for you (the docker and dockercompose triggers) or run a command. These are powerful and are deliberately not configured; read WUD's documentation before you enable one. The Watchers page shows the one watcher, local, reading this instance's Docker Engine:

The WUD Watchers page listing the local docker watcher

To watch Docker Engines on other hosts, add a watcher for each in /etc/wud/wud.env (WUD connects to a remote engine over TLS-protected TCP; never expose a Docker API without TLS).

Removing the demonstration. Once you watch your own containers, remove the demonstration container, its image, the two demonstration services and their settings:

sudo docker rm -f cloudimg-demo-app
sudo docker rmi 127.0.0.1:5000/cloudimg/demo-app:1.0.0
sudo systemctl disable --now cloudimg-wud-registry.service cloudimg-wud-sink.service
sudo sed -i '/^WUD_REGISTRY_CUSTOM_CLOUDIMGDEMO_/d; /^WUD_TRIGGER_HTTP_CLOUDIMGSINK_/d' /etc/wud/wud.env
sudo sed -i 's/^Wants=network-online.target cloudimg-wud-registry.service cloudimg-wud-sink.service$/Wants=network-online.target/' /etc/systemd/system/wud.service
sudo systemctl daemon-reload && sudo systemctl restart wud.service

Step 7: HTTPS certificate and host name

The certificate was created on your instance's first boot for the instance's public DNS name, public IP, private IP and localhost. The key, the credentials file, the session key and WUD's data directory are readable only by their owners, and the Docker socket only by root and the docker group:

sudo openssl x509 -in /etc/ssl/wud/wud.crt -noout -subject -ext subjectAltName
sudo stat -c '%a %U:%G %n' /etc/ssl/wud/wud.key /root/wud-credentials.txt /etc/wud/wud-secret.env /var/lib/wud /var/run/docker.sock
subject=CN = ec2-34-239-126-168.compute-1.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name: 
    DNS:ec2-34-239-126-168.compute-1.amazonaws.com, IP Address:34.239.126.168, IP Address:172.31.95.137, IP Address:127.0.0.1, DNS:localhost
600 root:root /etc/ssl/wud/wud.key
600 root:root /root/wud-credentials.txt
600 root:root /etc/wud/wud-secret.env
700 wud:wud /var/lib/wud
660 root:docker /var/run/docker.sock

To serve WUD under your own DNS name (for example wud.example.com, pointed at this instance), run the helper. It records the name, creates a new self-signed certificate for it, updates nginx and WUD's public URL, restarts WUD and rewrites the URL in the credentials file:

sudo wud-set-hostname wud.example.com

Without a recorded name, WUD follows the instance's public address after a stop and start. To use a certificate from your own certificate authority, replace /etc/ssl/wud/wud.key (keep it 0600 root) and /etc/ssl/wud/wud.crt, then run sudo systemctl reload nginx.

Step 8: Docker API, triggers and metadata

WUD and nginx cannot reach the instance metadata service, and the two demonstration services can reach nothing but loopback. The Docker API has no TCP listener, wud is the only member of the docker group, and the configured watcher, registry and trigger are the local ones described above. The firmware-metadata refresh is masked:

for u in wud nginx cloudimg-wud-registry cloudimg-wud-sink; do printf '%-22s %s\n' "$u" "$(systemctl show -p IPAddressDeny --value $u.service)"; done
TCP=$(sudo ss -Hltn | grep -cE ':(2375|2376)( |$)' || true); echo "Docker API TCP listeners: $TCP"
getent group docker
grep -E '^WUD_(TRIGGER|REGISTRY|WATCHER)_' /etc/wud/wud.env
FW=$(systemctl is-enabled fwupd-refresh.timer fwupd-refresh.service || true); echo "$FW"
[ "$TCP" = 0 ] && [ "$(getent group docker | cut -d: -f4)" = wud ] && [ "$(sort -u <<<"$FW")" = masked ] && ! grep -qE '^WUD_TRIGGER_(DOCKER|DOCKERCOMPOSE|COMMAND)_' /etc/wud/wud.env && [ "$(systemctl show -p IPAddressDeny --value wud.service | grep -c 169.254.169.254)" = 1 ]
wud                    169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
nginx                  169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
cloudimg-wud-registry  ::/0 0.0.0.0/0
cloudimg-wud-sink      ::/0 0.0.0.0/0
Docker API TCP listeners: 0
docker:x:987:wud
WUD_WATCHER_LOCAL_SOCKET=/var/run/docker.sock
WUD_WATCHER_LOCAL_CRON=0 * * * *
WUD_WATCHER_LOCAL_WATCHBYDEFAULT=true
WUD_REGISTRY_CUSTOM_CLOUDIMGDEMO_URL=http://127.0.0.1:5000
WUD_TRIGGER_HTTP_CLOUDIMGSINK_URL=http://127.0.0.1:3001/cloudimg-wud-trigger
WUD_TRIGGER_HTTP_CLOUDIMGSINK_METHOD=POST
masked
masked

(The two demonstration services allow loopback explicitly and deny everything else, which is why their deny list is "all addresses".)

Maintenance

  • Settings and secrets: WUD reads its configuration from /etc/wud/wud.env (world-readable) and from /etc/wud/wud-secret.env (root only, read by systemd before WUD starts). Put registry tokens, mail passwords and webhook secrets in a root-only file and add it to the service with a drop-in, then restart WUD:
sudo install -m 0600 /dev/null /etc/wud/my-secrets.env
sudoedit /etc/wud/my-secrets.env
sudo systemctl edit wud.service      # add:  [Service]  EnvironmentFile=/etc/wud/my-secrets.env
sudo systemctl restart wud.service
  • Password and users: change your password and create API tokens under your profile in the web interface (the avatar at the bottom left). Administrators can add users with read-only or administrator roles under Users. Update or delete /root/wud-credentials.txt after you change the password.
  • Backups: WUD's users, settings and history are in /var/lib/wud (SQLite). Back it up with WUD stopped; it holds the password hashes and API tokens, so keep the backup as safe as the credentials.
  • Upgrades: to move to a newer WUD release, launch the newer version from the listing when it is published and restore your backup. Docker Engine updates arrive through unattended-upgrades from Docker's repository.
  • Logs: sudo journalctl -u wud -n 100 --no-pager, and the web interface's Logs page.
  • Security patches: unattended-upgrades remains enabled for Ubuntu's security updates and for Docker Engine.

Security recommendations

  • Keep the security group tight. Open 22 only to your management address and 80/443 only to the people who use WUD. Anyone who can sign in sees this host's containers, images and labels.
  • Use your own certificate before other people sign in, so their browsers trust it without exceptions.
  • Keep WUD on loopback and reach it through nginx; nginx is what enforces the sign-in, keeps forwarded headers honest and refuses cross-site requests.
  • Think before enabling container-updating triggers. A docker or dockercompose trigger lets WUD replace running containers automatically; enable it only for containers where that is what you want.
  • Never publish the Docker API. If you watch other hosts, connect to their engines over TLS.

Support and licensing

WUD is developed by its contributors (the project was formerly known as What's Up Docker?) and distributed under the MIT License. Node.js is distributed under the MIT License, and Docker Engine and containerd under the Apache License 2.0. This image ships WUD 9.6.0 built from the official source with two small changes (WUD listens on loopback, and its session key is generated per instance), six updated dependencies and two upstream test directories removed; the build record, the list of changes and the licences are in /usr/share/doc/wud/. The demonstration registry, image and webhook receiver are written by cloudimg. cloudimg provides the packaging, the hardening, first-boot HTTPS and per-instance credentials, and 24/7 support.

cloudimg is not affiliated with or endorsed by the WUD project or its authors. "Docker" is a trademark of Docker, Inc., used here only to describe the container engine WUD monitors; cloudimg is not affiliated with, endorsed by, or sponsored by Docker, Inc. Product names are marks of their owners and are used here only to identify the software.

For help with deployment or configuration, email support@cloudimg.co.uk or visit cloudimg support.