The biggest barrier to actually practising web pen-testing is the friction of getting a vulnerable target running. Spin up a VM, install LAMP, copy DVWA in, configure the database, fight permissions, miss a step, start over. By the time you're actually attacking something it's 90 minutes later and your motivation is gone.
The Docker cyber lab solves that. docker compose up brings the whole environment online in under 30 seconds. docker compose down -v nukes it. The lab is reproducible, shareable, and lives entirely on its own bridge network so I can't accidentally point an exploit at the wider LAN.
The compose file
# docker-compose.yml — the entire lab
networks:
labnet:
driver: bridge
ipam:
config: [{ subnet: 172.20.0.0/24 }]
services:
kali:
image: kalilinux/kali-rolling
container_name: kali
networks: { labnet: { ipv4_address: 172.20.0.10 } }
cap_add: [ NET_ADMIN, NET_RAW ]
tty: true; stdin_open: true
volumes: [ ./loot:/loot ]
dvwa:
image: vulnerables/web-dvwa
container_name: dvwa
networks: { labnet: { ipv4_address: 172.20.0.20 } }
juiceshop:
image: bkimminich/juice-shop
container_name: juiceshop
networks: { labnet: { ipv4_address: 172.20.0.30 } }
pihole:
image: pihole/pihole
container_name: pihole
networks: { labnet: { ipv4_address: 172.20.0.40 } }
environment: { TZ: Europe/London, WEBPASSWORD: labpass }
Why each container is in there
| Kali | the attacker · burp, nmap, sqlmap, metasploit, hydra |
| DVWA | classic vulnerabilities · SQLi, XSS, CSRF, file upload, command inj |
| Juice Shop | modern OWASP top-10 · JWT, NoSQL, XXE, prototype pollution |
| Pi-hole | DNS observability · see what attack tools resolve |
DVWA covers the classics — the kind of vulnerability you find when you read SQL injection tutorials. Juice Shop is the modern flavour — JWT manipulation, NoSQL injection, prototype pollution, the bugs that show up in actual JavaScript/SPA codebases. Together they span what you actually see in the wild.
The isolated network is the point
The bridge network labnet has no route to the host. Containers can talk to each other, can't reach my LAN, can't reach the internet. This means I can run aggressive scans, fuzzers, exploit chains without worrying about collateral. If something escapes the target container, the worst it can do is hop to another container on the same isolated subnet.
The first time I ran sqlmap with default settings against a "test" target on my home LAN, it found a vulnerability in my router's admin page. Default sqlmap is aggressive. Network isolation is not paranoia — it's the difference between a lab session and an outage.
The Kali container is the workstation
Kali boots into a TTY. docker exec -it kali bash drops me in. Tools live where I expect them. The ./loot volume mount means anything I save is persistent across restarts — wordlists I've generated, scan reports, JWTs I've cracked. The container itself is ephemeral; the loot dir is mine.
[ TYPICAL SESSION ] $ docker compose up -d $ docker exec -it kali bash # nmap -sV 172.20.0.0/24 # what's alive? # nikto -h http://172.20.0.20 # DVWA recon # sqlmap -u "http://172.20.0.20/..." # SQLi automation # ./juice-shop-tools/dump-jwt.sh # JWT exfil $ docker compose down -v # done. clean again.
What it doesn't replace
The Docker lab is for web app and basic infra practice. It doesn't teach you Wi-Fi attacks (no radio), AD/Kerberos (need Windows containers + a domain), or kernel/binary exploitation (containers share the host kernel). For the things it covers — and that's 80% of what the average pen-test looks like — it's the fastest reset button I've ever set up.
Friction is the enemy of practice. Every minute of setup is a minute of motivation lost. The Docker lab is what made hands-on practice routine again — when the cost of starting is 30 seconds, you start.