Run a Small App Safely with docker-compose
You found a docker-compose.yml in a repo, pasted a command from newer docs, and it did not behave. This guide runs a small two-service application with the installed docker-compose command, checks what is running, and cleans up without deleting its data. Allow about 15 minutes if Docker is already installed and its daemon is available.
The route
Jump straight to the step you need, or tick off Done means at the end.
Before you start
This guide targets the installed Python-era command, docker-compose, which reports version 1.29.2. Docker now documents Compose v2 as the supported generation, invoked as docker compose. The hyphenated command is kept here because it is the installed program and the subject of the local manpage. Options and output can differ between the two generations.
You need Docker Engine running, permission to access its daemon, and a shell. Most commands below are ordinary user commands. Add sudo only if your local Docker setup requires it.
Security warning
Granting Docker daemon access is effectively privileged access.
Check the version before spending time debugging a command copied from newer documentation:
$ docker-compose --version
docker-compose version 1.29.2, build unknown
1. Create the Compose file
Make a new directory and enter it. The directory name becomes the default project name, so choose a name that will not collide with another application on this Docker host.
$ mkdir compose-demo
$ cd compose-demo
Create docker-compose.yml with two services:
webserves a small response on port 8080.workerprints a message and stays alive so that it is easy to inspect.
The depends_on entry controls start order, not application readiness. A dependent service can still need its own health or retry logic.
version: "3.8"
services:
web:
image: nginx:alpine
ports:
- "8080:80"
worker:
image: alpine:3.19
command: ["sh", "-c", "echo worker is running; sleep 3600"]
depends_on:
- web
Tip
Pinning image tags makes this example easier to repeat than using latest, but it does not make an image immutable: tags can still be moved in a registry. For a controlled deployment, review and pin image digests separately.
Ask Compose to parse and render the merged configuration before starting containers. This command is available in the installed 1.29.2 program even though it is absent from the older local manpage:
$ docker-compose config
Checkpoint
Expected output includes services:, followed by web: and worker:. Fix a YAML indentation error or an unavailable field here, before anything is started.
2. Start the project
Start both services in the foreground first. Compose creates or recreates containers, attaches their output, and normally stops them when you interrupt the command.
$ docker-compose up
On the first run, Docker may pull the two images. You should see creation or startup messages and then output from worker. In another terminal, verify the project without interrupting it:
$ cd compose-demo
$ docker-compose ps
Checkpoint
Both services should appear with a running state.
Recovery
The web container now occupies the host port, so another application already listening on 8080 will make startup fail. Change the left side of "8080:80", for example to "18080:80", then run docker-compose up again.
Press Ctrl-C in the foreground terminal to stop the attached run. This stops the containers but does not remove their definitions or images. Restart existing containers later with:
$ docker-compose start
$ docker-compose ps
3. Run detached and inspect logs
For a background project, use detached mode. This command changes host state and leaves both containers running after your shell returns:
$ docker-compose up -d
Verify the result before using the application:
$ docker-compose ps
$ curl http://127.0.0.1:8080/
The second command should return the Nginx welcome response. If curl is not installed, use a browser or another HTTP client. To investigate startup or application output, show the project logs without colour codes:
$ docker-compose logs --no-color
$ docker-compose logs --no-color web
To find the host port from the Compose definition rather than assuming it, ask for the published port. The service port is 80 and the protocol defaults to TCP:
$ docker-compose port web 80
Expected output has the form 0.0.0.0:8080 or an equivalent host address and port. If you run multiple instances of a service, the manpage's --index option selects which container to inspect.
4. Run a one-off command without changing the service
run creates a temporary container using a service's configuration and replaces that service's command. It does not publish the service's configured ports unless you explicitly add --service-ports, which prevents accidental port collisions.
$ docker-compose run --rm worker sh -c 'printf "one-off check\n"'
one-off check
The --rm option removes that one-off container after the command exits. It is useful for checks and migrations, but do not use it as a substitute for an explicit backup or rollback plan.
Tip
Use --no-deps when the command must not start linked services.
5. Stop, remove and recover
When you are finished testing, stop the running containers without deleting them:
$ docker-compose stop
$ docker-compose ps
Start them again with docker-compose start. If you want to remove stopped service containers, use:
$ docker-compose rm
Compose asks for confirmation. Add -f only when that removal is deliberate. The example above has no named volume, so it has no application data to preserve.
Warning
The local manpage documents -v with rm to remove volumes associated with the containers. Treat that option as destructive, and do not use it until you have confirmed that the volumes contain no data you need.
To undo the demo completely, stop it and remove its containers. If you changed the file or image and want Compose to apply the change, plain up may recreate existing containers. Use --no-recreate when reusing the existing containers is the safer choice, or --no-build when a missing image must not trigger a build.
$ docker-compose stop
$ docker-compose rm
$ docker-compose ps
Checkpoint
An empty service listing confirms that the demo containers are gone. The downloaded images remain until you remove them with Docker's own image-management commands, and keeping them is usually the quickest recovery if you intend to repeat the test.
Common traps
- Wrong project. Compose groups resources by project name. Run commands from the directory containing the intended file, or set
-p PROJECT_NAME. The environment variableCOMPOSE_PROJECT_NAMEprovides the same project-name override. - Wrong file. The default is
docker-compose.yml. Use-f PATHfor a different file.COMPOSE_FILEcan provide the file through the environment, but an explicit command-line choice is easier to see during recovery. - Service is running but not ready. Start order does not prove readiness. Make the application retry its dependency or add an appropriate health check supported by the Compose version you are using.
- Port already allocated. Change the host side of the mapping, then recreate the affected service. Do not stop an unrelated production service merely to make a demo start.
- Daemon connection failure. Inspect
DOCKER_HOST. The manpage's default isunix:///var/run/docker.sock;DOCKER_TLS_VERIFYandDOCKER_CERT_PATHalter TLS connections. Avoid copying certificates or using an insecure registry option into a script without understanding the security impact.
Done means
- Version known.
docker-compose --versionidentified the installed command generation. - YAML accepted.
docker-compose configparsed the file without complaint. - Services running.
docker-compose psshowed the expected services in a running state. - Web reachable.
curl http://127.0.0.1:8080/or an equivalent check reached the web service. - Cleanup was deliberate. You stopped or removed the project on purpose, and did not use volume removal without checking for data.