docker compose down stops and removes your entire Docker Compose stack — containers, networks, and optionally volumes and images. It sounds simple, but picking the wrong flags can mean data loss or leaving gigabytes of stale images on your server.
This guide covers every flag, real-world patterns, and the exact difference between down, stop, and rm.
What docker compose down Actually Does
When you run docker compose down, Docker Compose executes a four-step sequence:
- Sends
SIGTERMto all running containers in the stack - Waits for them to exit gracefully (default: 10 seconds)
- Sends
SIGKILLto any container that didn’t stop in time - Removes the stopped containers and the default network
What it does not do by default: delete your named volumes or images. Your database data is safe unless you explicitly tell it otherwise.
docker compose down
Output looks like this:
[+] Running 3/3
✔ Container myapp-web-1 Removed 0.4s
✔ Container myapp-db-1 Removed 1.2s
✔ Network myapp_default Removed 0.1s
Every Flag Explained
—volumes / -v
Named volumes persist across docker compose down by default. Add -v to delete them:
docker compose down -v
This permanently destroys all data stored in named volumes. That includes your Postgres database, Redis cache, uploaded files — anything stored in a Docker-managed volume.
Use this when:
- You want to reset a development database to a known state
- A CI/CD run should be completely isolated from the previous one
- You’re decommissioning a service and want to clean up
Never run down -v on a production database unless you have a verified backup.
—remove-orphans
When you remove a service from docker-compose.yml but forget to stop the old container, Docker Compose calls it an “orphan” — a container that no longer has a matching service definition.
docker compose down --remove-orphans
Docker Compose warns you about orphans but won’t remove them unless you ask. Add this flag any time you restructure your compose file.
—rmi (Remove images)
Two options here:
# Only remove images that were built locally (not pulled from a registry)
docker compose down --rmi local
# Remove every image used by any service in the compose file
docker compose down --rmi all
Use --rmi all when you want the next docker compose up to pull fresh versions of all images. Useful before a deploy to verify your image tags are correct.
—timeout / -t
Docker waits 10 seconds for a graceful shutdown before force-killing. If your app needs longer to drain connections:
docker compose down -t 30
Applications that handle long-running HTTP requests, database transactions, or message queue consumers typically need more than 10 seconds to shut down cleanly.
—dry-run
New in recent Docker Compose versions — preview what would be removed without actually doing it:
docker compose down --dry-run
Use this before running a destructive down command to confirm you’re targeting the right stack.
docker compose down vs stop vs rm
These three commands are frequently confused:
| Command | Stops | Removes containers | Removes networks | Removes volumes |
|---|---|---|---|---|
stop | ✅ | ❌ | ❌ | ❌ |
rm | ❌ | ✅ (only stopped) | ❌ | optional |
down | ✅ | ✅ | ✅ | optional (-v) |
Use stop when you want to pause the stack temporarily. The containers still exist; you can inspect them, check logs, and restart with docker compose start.
Use down for a clean teardown. It’s the mirror image of up.
Use rm when you’ve already stopped containers and want to remove them without touching networks.
Combining Flags for Different Scenarios
Development: complete environment reset
docker compose down -v --remove-orphans
Removes containers, networks, and all data volumes. Your next up starts with a fresh database.
CI/CD: maximum isolation
docker compose -f docker-compose.test.yml down -v --rmi local --remove-orphans
Removes everything created during the test run, including locally built images.
Production maintenance: clean restart, preserve data
docker compose down --remove-orphans -t 60
Removes containers and networks, keeps volumes, gives services 60 seconds to shut down gracefully.
Full cleanup: remove everything including pulled images
docker compose down -v --rmi all --remove-orphans
Use with caution — this forces full image re-downloads on the next up.
Working with Multiple Compose Files
If you’re using environment-specific compose files:
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml down
# Staging
docker compose -f docker-compose.yml -f docker-compose.staging.yml down -v
The -f flag must come before the subcommand.
Removing a Single Service Without Down
docker compose down operates on the entire stack. To remove one container without touching the others:
# Stop and remove a specific service
docker compose rm -sf web
The -s flag stops it first, -f skips the confirmation prompt.
Or to stop a service and remove it cleanly:
docker compose stop web
docker compose rm web
Bind Mounts vs Named Volumes
Understanding this distinction prevents data loss:
services:
web:
volumes:
- ./src:/app/src # bind mount — always safe, never deleted
- node_modules:/app/node_modules # named volume — deleted by down -v
db:
volumes:
- postgres_data:/var/lib/postgresql/data # named volume — deleted by down -v
volumes:
node_modules:
postgres_data:
docker compose down -v only deletes named volumes (those declared under the top-level volumes: key). Bind mounts — paths starting with ./ or / — are never touched.
Verifying What Will Be Deleted
Before running a destructive down, inspect your volumes:
# List volumes in the current stack
docker compose config --volumes
# Inspect a specific volume
docker volume inspect myapp_postgres_data
# Check volume size
docker system df -v
To back up a Postgres database before wiping:
docker compose exec db pg_dump -U postgres mydb > backup_$(date +%Y%m%d).sql
docker compose down -v
Troubleshooting: Containers Won’t Stop
If containers ignore SIGTERM and timeout hits:
Option 1 — Increase the timeout:
docker compose down -t 60
Option 2 — Fix signal handling in your Dockerfile:
# Use exec form (correct — PID 1 receives signals)
CMD ["node", "server.js"]
# Shell form (wrong — shell catches signals, not your app)
CMD node server.js
Option 3 — Specify the correct stop signal:
STOPSIGNAL SIGUSR2
Some frameworks (like Node.js with pm2, or certain Java apps) respond to different signals.
Automating Cleanup in Scripts
For automated maintenance scripts:
#!/bin/bash
set -e
echo "Backing up database..."
docker compose exec -T db pg_dump -U postgres mydb > /opt/backups/db_$(date +%Y%m%d_%H%M%S).sql
echo "Stopping stack..."
docker compose down --remove-orphans
echo "Pruning unused images..."
docker image prune -f
echo "Done."
Running Docker on a VPS
All these examples assume a Linux server running Docker. If you need a VPS to host your Docker Compose stack, Hetzner Cloud offers the best price-to-performance ratio in Europe — a CX22 server (2 vCPU / 4 GB RAM) costs €4.15/month and handles most Docker workloads comfortably. New accounts get €20 in free credits.
For US-based infrastructure, DigitalOcean provides 1-click Docker Droplets starting at $6/month with $200 in free credits for new accounts. Vultr is another solid option with global data centers and $100 in free credits.
Quick Reference
| Goal | Command |
|---|---|
| Stop stack, keep data | docker compose down |
| Stop stack + delete volumes | docker compose down -v |
| Remove orphan containers too | docker compose down --remove-orphans |
| Force fresh image pulls | docker compose down --rmi all |
| Full reset | docker compose down -v --rmi all --remove-orphans |
| Custom timeout | docker compose down -t 30 |
| Use a specific compose file | docker compose -f file.yml down |
Summary
docker compose down is safe by default — it removes containers and networks but never touches your data volumes. Add -v only when you intentionally want to wipe data. In daily development workflows, plain docker compose down followed by docker compose up -d is all you need. Save the -v --rmi all combination for CI pipelines and full resets.
The most important habit: always check docker compose config --volumes before running down -v so you know exactly what data you’re about to delete.