Install the agent with Docker
The Docker variant of the Retrievy Agent runs on any host that can pull from Docker Hub. It speaks the same wire protocol as the Windows MSI agent and shows up in the same Fleet → Agents page in Retrievy.
It's the right pick when you want to scan FortiGate firewalls from a Linux jump host, when your environment is fully containerised, or when you simply don't have a Windows server available.
The Docker agent scans FortiGate only today (SSH + REST API). Active Directory scanning is Windows-only. If you need AD coverage, use the Windows MSI install on a domain-joined Windows server instead.
Before you start
You need:
- A Linux host (or any OS) with Docker installed. The image targets
linux/amd64andlinux/arm64. - Outbound HTTPS (port 443) from the host to your Retrievy workspace. No inbound ports are required.
- SSH and HTTPS reachability from the host to whichever FortiGate firewalls you want scanned, plus the FortiGate read-only API credentials (handled separately, not in this guide).
- A Retrievy account with the Manage Agents permission. Without it the New Token button on the Agents page is disabled.
The container runs as a non-root user (UID 65532) on a read-only root filesystem. The only writable path is a Docker named volume (retrievy-agent-state) where the agent persists its RSA key and session token. You don't need to grant any extra capabilities.
Step 1. Generate an install token
In Retrievy:
- Open Fleet → Agents in the navigation.
- Click New Token in the top-right.
- In the modal, pick Docker Agent under Runtime.
- Enter an Agent Name that identifies the host (something like
fortigate-scanner-euworks well). - Click Generate Token.
The token appears once. Copy it now. You won't be shown it again.
The token is one-time-use and expires in 60 minutes. If the timer runs out before you finish, rotate the token from the same page and start over.
Step 2. Start the container
On the host with Docker, run:
# Start the Retrievy Agent as a hardened container
docker run -d --name retrievy-agent \
--restart unless-stopped \
--read-only \
--cap-drop=ALL \
--security-opt no-new-privileges \
--user 65532:65532 \
-v retrievy-agent-state:/var/lib/retrievy \
-e RETRIEVY_TOKEN=rv_install_xxxxxxxxxxxxxxxxxxxx \
-e RETRIEVY_HOST=https://<your-workspace>.retrievy.com \
docker.io/retrievy/retrievy-agent:latest
Replace the RETRIEVY_TOKEN with the install token you just generated and <your-workspace> with your workspace subdomain.
What each flag does, in case your team needs to justify them:
| Flag | Why |
|---|---|
--restart unless-stopped | Auto-restart on host reboot. The agent also exits with code 75 when an update is available; restart picks up the new image. |
--read-only | The agent does not write to its root filesystem. The only writable path is the state volume. |
--cap-drop=ALL | No Linux capabilities. The agent makes outbound TCP and reads its volume; nothing more. |
--security-opt no-new-privileges | The process cannot gain new privileges via setuid binaries. |
--user 65532:65532 | Nonroot user shipped in the distroless base image. |
-v retrievy-agent-state:/var/lib/retrievy | Persists the RSA key, session token, and manifest cache across container restarts. Pre-chowned to UID 65532 at image build time. |
If you prefer Docker Compose, the equivalent service is:
services:
retrievy-agent:
image: docker.io/retrievy/retrievy-agent:latest
restart: unless-stopped
read_only: true
cap_drop: ["ALL"]
security_opt: ["no-new-privileges:true"]
user: "65532:65532"
volumes:
- retrievy-agent-state:/var/lib/retrievy
environment:
RETRIEVY_TOKEN: "rv_install_xxxxxxxxxxxxxxxxxxxx"
RETRIEVY_HOST: "https://<your-workspace>.retrievy.com"
volumes:
retrievy-agent-state:
Step 3. Verify the agent is online
Back in Retrievy, on the Fleet → Agents page, the new host appears in the fleet list with a Docker type badge and one of the standard status badges (Online, Outdated, Offline, Pending, Revoked). The fleet summary tiles at the top of the page count Docker and Windows agents separately (you'll see something like Windows 3 / Docker 1).
If the row reads Online within five minutes, you're done. The first FortiGate scan starts as soon as you finish wiring up FortiGate credentials in the Retrievy app (covered separately in the FortiGate integration guide).
What the container does on first boot
- Reads
RETRIEVY_TOKENandRETRIEVY_HOSTfrom environment variables. - Generates an RSA-4096 key pair locally and writes the private key to the state volume.
- Registers with Retrievy using the install token, exchanges it for a permanent session token, and submits the public key.
- Clears the install token from memory and the environment record.
- Begins the 10-minute heartbeat loop.
After registration, the install token is useless. The container only uses the long-lived session token (in the state volume) for everything from there on.
Auto-update with Watchtower
The agent ships its current version on every heartbeat. When Retrievy publishes a newer build, the heartbeat response includes an update directive. The agent exits with code 75, and --restart unless-stopped brings it back.
Bringing the container back, however, only picks up a new image if the host's local image was updated. For that, run the Retrievy Watchtower companion container alongside the agent:
docker run -d --name retrievy-watchtower \
--restart unless-stopped \
-v /var/run/docker.sock:/var/run/docker.sock \
docker.io/retrievy/agent-watchtower:latest
It polls Docker Hub every five minutes for new image digests, pulls when one lands, and recreates the retrievy-agent container. If you'd rather pin a specific version and update on your schedule, skip Watchtower and run a manual docker pull docker.io/retrievy/retrievy-agent:<version> then docker compose up -d.
Status states
In Retrievy, on the Fleet → Agents page, the Docker agent uses the same badges as the Windows agent:
| Badge | Meaning |
|---|---|
| Online (green, pulsing) | Heartbeat in the last 10 minutes. |
| Outdated (amber) | Reachable but on an older image. Watchtower (or your manual pull) will pick up the new one. |
| Offline (gray) | No heartbeat in over 20 minutes (two missed heartbeats). |
| Pending | Install token generated but no container has registered with it yet. |
| Revoked (red) | The agent has been explicitly revoked from this page. |
Removing the agent
To stop scanning but keep the credentials:
docker stop retrievy-agent
To remove the agent entirely:
docker stop retrievy-agent
docker rm retrievy-agent
docker volume rm retrievy-agent-state
Then on the Retrievy Fleet → Agents page, click Revoke on the corresponding row so the token can no longer authenticate.
Troubleshooting
Symptom: The container starts but the Retrievy fleet list stays on Pending.
→ Fix: The agent never reached Retrievy. From inside the host, test reachability: curl -I https://<your-workspace>.retrievy.com. If that fails, your egress firewall or proxy is blocking outbound 443.
Symptom: Container starts then immediately exits with code 75.
→ Fix: That's the update sentinel. The agent told itself to restart so a newer image can take over. With --restart unless-stopped plus Watchtower (or any image-puller), this is normal. If you see it loop without making progress, run docker pull docker.io/retrievy/retrievy-agent:latest manually and recreate the container.
Symptom: Container starts but docker logs retrievy-agent shows signature verification failed.
→ Fix: Retrievy rejected the RSA proof-of-possession on a recent heartbeat. Usually this means the state volume was wiped (or it's a fresh container reusing an old volume from a different agent). Delete the volume (docker volume rm retrievy-agent-state), generate a fresh install token, and start over.
Symptom: FortiGate scans never finish.
→ Fix: The container must reach the FortiGate's SSH port (default 22) and HTTPS API. From inside the container: docker exec retrievy-agent /retrievy-agent --check-network. If that's not available in your version, test from the host: nc -vz <fortigate-ip> 22 and curl -I https://<fortigate-ip>/api/v2/cmdb/system/status.
Symptom: Agent shows Outdated for hours and Watchtower is running.
→ Fix: Watchtower needs the Docker socket mounted (-v /var/run/docker.sock:/var/run/docker.sock) and outbound 443 to Docker Hub. Both have to work. Check docker logs retrievy-watchtower for the last poll result.