A free vulnerability scanner deployed as a Portainer stack on your existing Proxmox VM. No new VM, no ISO, no Docker install — you already did that part.
Seven steps, all inside the VM you already have. Rolling updates, full shell access, no extra hardware.
Start here →Greenbone's own prebuilt VM as an OVA. Less setup, but needs an email request and 500 GB of disk.
See Path B →Two places: the VM's shell (SSH or Proxmox console) and the Portainer web UI. Blocks with a copy button are meant to be pasted.
The scanner is the biggest thing most homelab VMs ever run. Three checks before you start:
free -h
df -h /var/lib/docker
ss -tlnp | grep -E ':(443|9392)\b' || echo "443 and 9392 are free"
| Check | What you need |
|---|---|
| RAM | 8 GB minimum, 16 GB comfortable. 4 GB OOM-kills gvmd during the first sync. |
| Free disk | 40 GB+ on the Docker volume. Images ~8 GB, feed data ~32 GB measured. |
| Ports | 443 and 9392 free on the VM. Portainer's own 9000/9443 don't conflict. |
You have ≥8 GB RAM, ≥40 GB free, and both ports show nothing listening.
Greenbone ships one rolling compose file. It binds the web UI to 127.0.0.1 only, and the UI is HTTPS on 443 while 9392 just redirects to 443 — so both ports need publishing or the browser lands nowhere.
mkdir -p ~/greenbone && cd ~/greenbone
curl -fsSLO https://greenbone.github.io/docs/latest/_static/compose.yaml
sed -i 's|- 127.0.0.1:9392:9392|- 9392:9392|; s|- 127.0.0.1:443:443|- 443:443|' compose.yaml
grep -nE '443|9392' compose.yaml
The grep prints - 9392:9392 and - 443:443, neither with 127.0.0.1 in front.
cat compose.yaml copies the whole thing for you.greenbone.cat compose.yaml) into the editor.Portainer pulls ~20 images on first deploy — several GB, 5–20 minutes. The stack page shows each container going from created to running; don't redeploy while it's pulling.
Stacks → greenbone lists ~20 containers, most running, none stuck restarting for more than a minute.
name: delete the first line of the file (name: greenbone-community-edition) and deploy again. That key is standard docker compose, but older Portainer versions reject it — the stack name you typed does the same job.deploy: keys behave differently and the self-signed cert setup may not come up cleanly. This guide assumes the normal Docker Standalone environment — check Environment details if unsure.Default is admin/admin. Portainer's console runs as root, but gvmd must run as the gvmd user — so this one is a VM shell command (SSH in, or Proxmox → Console):
docker exec -u gvmd "$(docker ps -qf name=gvmd | head -1)" \
gvmd --user=admin --new-password='PickSomethingLong!42'
No output, no error. Replace the password inside the single quotes — a $ in it will otherwise be eaten by the shell.
This is the slow part and where people give up. Greenbone loads ~100k vulnerability tests plus CVE/CPE data into Postgres. Watch it in Portainer: Stacks → greenbone → the ospd-openvas container → Logs, and wait for:
Finished loading VTs. The VT cache has been updated from version X to Y.
Then check the gvmd container's logs for the last gate:
Updating VTs in database ... done (XXXXX VTs).
Prefer the shell? Both at once:
docker logs -f "$(docker ps -qf name=ospd-openvas | head -1)" 2>&1 | grep --line-buffered -i "loading VTs"
docker logs -f "$(docker ps -qf name=gvmd | head -1)" 2>&1 | grep --line-buffered -i "Updating VTs in database"
You saw "Finished loading VTs" and "Updating VTs in database ... done".
From any machine on your LAN:
https://192.168.50.XXX/
Log in as admin with the password from step 4. The cert is generated by the stack's gvm-config container and self-signed, so expect a browser warning → Advanced → Proceed.
You get the Greenbone dashboard, not a connection error.
- 8443:443) and always browse https://<vm-ip>:8443/ directly — don't use :9392, because its redirect points at 443 and will fail.curl -kI https://localhost. If that works but the LAN can't reach it, re-check step 2 — the usual cause is publishing 9392 but leaving 443 on 127.0.0.1.192.168.50.38 — your NPM host).One host takes roughly 10–40 minutes depending on what's listening.
A report exists with findings (or a clean bill of health).
The stack is a rolling release — Greenbone ships one unversioned compose file with moving stable image tags, so "update" means: refresh the file, paste, redeploy. Do this every few weeks.
sed edits — the download overwrites them).If the version numbers in the UI haven't moved afterwards, the images weren't re-pulled. Force it from the VM shell, then update once more:
for i in gvmd gsa gsad openvas-scanner ospd-openvas pg-gvm; do
docker pull registry.community.greenbone.net/community/$i:stable
done
Skip this unless you specifically want the vendor appliance or you're evaluating what a paid OPENVAS tier looks like. It is a different product from what Path A deploys.
Not a direct link: fill the form at greenbone.net/en/openvas-free/ (email + country + captcha) and they email the OVA. Minutes to a working day.
You have an .ova file, a few GB.
Proxmox imports OVAs only from a file-based storage with the Import content type (wizard added in PVE 8.2).
Datacenter → Storage → Add → Directory → path e.g. /var/lib/vz/import (create it first on the node shell: mkdir -p /var/lib/vz/import), content types: tick Import and Disk image.
Drop the OVA in that storage's import/ subfolder, or upload via Storage → Import → Upload.
The OVA appears in that storage's Import tab.
Select the OVA → Import → wizard:
After import: Options → Boot Order = imported disk, vmxnet3 on vmbr0, then start it and open Console.
You reach the GOS administration menu (text-based) instead of a boot error.
Console login admin / admin, then the first-login wizard:
The console shows the appliance IP on login. Shut down via Maintenance → Power — not by yanking the VM, the feed updater dislikes it.
Self-check passes and the GOS menu shows the web UI URL.
Browse to https://<appliance-ip>, accept the self-signed cert, log in with the web admin you created. Then Scans → Tasks → + New Task against your own LAN, same workflow as step 7.
Dashboard loads and a task runs to Done.