Hey everyone. Today we’re setting up What’s Up Docker (WUD for short), a little self-hosted tool that keeps an eye on your Docker containers and tells you when a newer version of an image is available. I installed it on my home server last week and it’s freaking fantastic, so I wanted to write up how I got it going.
I’m also writing this because I hit two little snags the quick-start guides skipped right over, and I figured I’d save you the trouble of finding them the hard way like I did. The first is that the image name everyone links to has quietly moved. The second is a sneaky firewall thing where Docker ignores your firewall rules without telling you, which is exactly the kind of surprise you don’t want on a machine facing the internet. We’ll sort out both as we go.
If you just want the install and don’t care about the backstory, feel free to skip down to the Installing WUD section.
What You’ll Need
- A Linux machine running Docker and Docker Compose. A Raspberry Pi is totally fine for this, it barely uses any resources.
- Terminal access to that machine (SSH is great).
- About ten minutes.
Why You’d Want This
Here’s the gap Docker leaves you with: it will happily run a two-year-old image forever and never say a word about it. There’s no little sticker on the container telling you it’s past its expiration date. You either remember to go check every image by hand, or you don’t, and your stack slowly turns into a fridge full of stuff you’re scared to open.
WUD is the friend who walks through that fridge for you and reads the dates off everything out loud. It watches your running containers, checks where each image came from, and lets you know when there’s something newer waiting.
The part I like most: by default it tells you and that’s it. It does NOT go updating things behind your back. You stay in the driver’s seat and decide when to pull the trigger on an update. You can wire up auto-updates later if you really want them, but out of the box it’s a polite heads-up, not a surprise.
How It Works (the 30-second version)
There are three pieces to WUD, and the names are pretty literal:
- Watchers look at your Docker machine and find the containers running on it.
- Registries are the places your images came from, like Docker Hub. WUD asks them “got anything newer for these?”
- Triggers are what happens when the answer is yes. Send yourself an email, ping a Discord channel, fire off a Telegram message, whatever fits how you like to be bugged.
You don’t need to touch most of this to get started. Point WUD at your machine and it watches everything by default. We’ll tighten it up afterward.
One Quick Heads-Up Before We Install (the image moved)
This is snag number one. A ton of older guides, including the first version of my own compose file, point at an image called fmartinou/whats-up-docker. That image still downloads, but the project packed up and moved house. The current one lives at:
- Docker Hub:
getwud/wud - GitHub Container Registry:
ghcr.io/getwud/wud
So use getwud/wud. If you’re already running the old one like I was, just swap the image name so you keep getting updates. (Yes, an update-notifier that needs to be told to update itself is a little funny. I had a chuckle too.)
Installing WUD
Here’s my compose file. I keep WUD’s data in a folder right next to where it lives, at /home/your-user/docker/wud/, so everything for the service sits in one tidy spot. Swap your own username in for your-user.
services:
wud:
image: getwud/wud:latest
container_name: wud
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /home/your-user/docker/wud:/store
environment:
- WUD_SERVER_PORT=3008
- WUD_WATCHED_LOCAL_CONNECTION=unix:///var/run/docker.sock
ports:
- "3008:3008"
restart: unless-stopped
Let me walk through the lines that matter so you know WHY they’re there, not just that they are:
- The
:roon the Docker socket. WUD only needs to read what’s running, it doesn’t need to change anything, so we mount the socket read-only. It’s a nice safe default. (If you turn on auto-update triggers later, you’ll need to drop the:ro, but start locked down.) - The
/storevolume. This is WUD’s memory. Without it, every time the container restarts it forgets all your settings and history. The volume is what makes them stick around. - Port 3008. WUD listens on port 3000 by default, but I already had something parked on 3000, so I moved it to 3008. The only rule is to keep
WUD_SERVER_PORTand the published port matching each other.
Bring it up with:
docker compose up -d
Then open http://your-server-ip:3008 in a browser. You should see the dashboard with all your containers listed out and their update status. That’s the whole basic install. Pretty painless.
Telling WUD What to Watch
Watching everything is a fine way to start. When you want more control, you add labels to the containers you actually care about. For example:
labels:
- wud.watch=true
- wud.tag.include=^\d+\.\d+\.\d+$
That regex on the second line looks scarier than it is. All it’s saying is “only count proper version numbers like 1.4.2 as updates, and ignore the noise like latest, nightly, and dev.” That one line cuts out a LOT of useless notifications once you’re past the first day, so it’s worth adding early.
Getting Notifications Going (Triggers)
A notifier you have to log in and check isn’t doing much for you. Triggers are how WUD reaches out to you, and they’re just more environment variables on the WUD container. Here’s a Telegram example to show you the shape of it:
environment:
- WUD_TRIGGER_TELEGRAM_MYBOT_BOTTOKEN=123456:abcdef
- WUD_TRIGGER_TELEGRAM_MYBOT_CHATID=987654321
The naming pattern is WUD_TRIGGER_<TYPE>_<YOURNAME>_<SETTING>, and the same idea works for email, Discord, Slack, Pushover, MQTT, and more. Pick whichever one you’ll actually pay attention to, restart the container, and updates start landing in your pocket.
Locking the Door with a Password (Basic Auth)
The WUD dashboard shows anyone who can reach it exactly which images you’re running and which ones are behind on updates. That’s basically a to-do list for someone looking for a way in, so let’s not leave it sitting out. At a minimum, turn on a password:
environment:
- WUD_AUTH_BASIC_MYUSER_USER=admin
- WUD_AUTH_BASIC_MYUSER_HASH=$$apr1$$....your-hash-here....
The hash is an Apache-style (htpasswd) hash. You can generate one with:
htpasswd -nbB admin 'your-password-here'
Take the part after the username and drop it into the _HASH variable. Here’s the one that gets EVERYBODY: inside a compose file you have to write every $ as $$, otherwise Compose tries to treat your hash as a variable and your login silently breaks. Ask me how I know.
Setting Up the Firewall (UFW) on Ubuntu, Linux, and the Raspberry Pi
This is the section folks tend to skip, and it’s the one that comes back around to bite them. We’re using UFW, which stands for Uncomplicated Firewall, and it honestly earns the name. It’s the friendly front-end that sits on top of the messy iptables stuff so you don’t have to think about it.
The Normal Setup
On Ubuntu, UFW is usually already installed. On a Raspberry Pi or a minimal Debian box, grab it first:
sudo apt update
sudo apt install ufw
Now, before we touch anything else, let’s make sure you don’t accidentally lock yourself out over SSH. This is THE classic way people strand a headless Pi sitting in a closet with no keyboard attached, and it makes for a real sad afternoon. Allow SSH first, every time:
sudo ufw allow OpenSSH
# or, if you run SSH on a custom port:
# sudo ufw allow 22/tcp
Set your defaults so everything coming in is blocked and everything going out is allowed:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Now let WUD through, but only from your home network instead of the entire internet. Adjust the subnet to match yours (192.168.1.0/24 is a common one):
sudo ufw allow from 192.168.1.0/24 to any port 3008 proto tcp
Flip it on and check your work:
sudo ufw enable
sudo ufw status verbose
You should see your SSH and WUD rules listed out. On a Raspberry Pi it’s the exact same commands, there’s nothing Pi-specific to learn, and you don’t even need to reboot for it to take effect.
The Sneaky Part: Docker Strolls Right Past UFW
Okay, this is snag number two, and it’s a real head-scratcher the first time you run into it. Here’s the thing worth taping to your monitor: when you publish a container port with Docker, UFW does not actually protect it.
You can run sudo ufw deny 3008, watch it show up in the list as denied, feel good about yourself, and your dashboard is STILL reachable from outside. This isn’t a bug in either tool. Docker writes its own firewall rules and slips them in ahead of UFW’s, so your traffic gets to the container before UFW ever gets a chance to look at it.
So that nice “only my home network” rule we just wrote? For a port published as 3008:3008 (which opens it on every network interface), it might not be doing a thing. Here’s how to actually close the gap, easiest option first.
Option 1, and the one I’d reach for: bind the port to an interface you trust.
This is the cleanest fix for a dashboard you only poke at from home. Instead of publishing to everything, publish only to localhost, or to one specific home IP, right in your compose file:
ports:
# Only reachable from the machine itself (great if you put a reverse proxy in front):
- "127.0.0.1:3008:3008"
# ...or only on your home network IP:
# - "192.168.1.50:3008:3008"
Since Docker now only opens that port on the one interface, there’s nothing exposed to the outside world and no fight with UFW to have in the first place. For a home dashboard, this is usually the whole answer.
Option 2: use the DOCKER-USER chain.
Docker actually leaves you a special chain called DOCKER-USER on purpose, and rules you put there get checked before Docker’s own. If you need a port published to everything but still want to control who reaches it, this is where those rules go. You don’t need this for a basic WUD setup, but it’s good to know it exists.
Option 3: let ufw-docker handle it for you.
There’s a tidy little community project called ufw-docker that stitches UFW and Docker together properly, without breaking Docker’s networking. The solution is located here: chaifeng/ufw-docker. Once it’s set up, you manage container ports with commands like:
sudo ufw-docker allow wud 3008/tcp
If you run a bunch of published containers and want UFW to be the real boss of all of them, this is the sturdier long-term setup.
Honestly, the Nicest Option: Don’t Open the Port at All
If you want to reach WUD from outside the house, the calmest path isn’t poking a hole in your firewall at all. Put WUD behind a reverse proxy like Nginx Proxy Manager, Caddy, or Traefik, which handles the HTTPS and adds its own login. Pair that with the 127.0.0.1:3008:3008 binding from Option 1 and your dashboard is encrypted, password-protected, and never sitting directly out in the open. I can show you how to set that up sometime if there’s interest.
Final Thoughts
WUD turned into one of my favorite little home-lab tools really fast, because it does one boring job and does it well: it takes the guesswork out of “is anything around here out of date?” Add a notification trigger and a properly set up firewall, and you’ve basically got a maintenance habit that runs itself.
The two things I’d ask you to take away, since they’re the bits the quick-start guides gloss right over: use the getwud/wud image, and remember that Docker walks straight past UFW unless you bind your ports on purpose. Get those two right and you’re golden.
That’s it! If you get stuck on a trigger or the firewall part trips you up, leave a comment and I’ll help you sort it out. Thanks for reading, and happy self-hosting.
-Adrian
