You have an image — now you need to run it. In this article we will walk through the docker run command, the flags you use every day, and what happens to a container after it starts: how to view logs, step inside, and stop it cleanly.
The expensive part here is not the start but the stop: docker stop does not switch the container off instantly, it gives the process 10 seconds, and only the application itself can make use of them.
docker stop does not switch the container off, it hands the process a 10-second budget. Catch SIGTERM and you finish in time and exit on your own; ignore it and you get SIGKILL together with cut-off requests.
What happens on start
When you type docker run, Docker creates a container from the image — an isolated process with its own file system, network, and environment variables — and starts it.
Short formula: an image is the recipe, a container is the cooked dish.
The simplest run:
docker run eclipse-temurin:21-jre java -version
Docker pulls the image (if it is not present locally), creates a container, runs the command, and exits. The container stops as soon as the process inside it finishes.
Core docker run flags
Most real-world runs use several flags together. Let's go through each one.
-p: port mapping
A container lives in an isolated network — you cannot reach it from the outside until you explicitly open a port.
docker run -p 8080:8080 my-spring-app
# ^ ^
# host:container
The left side is the port on your machine (the host), the right side is the port inside the container. You can use different numbers:
docker run -p 9090:8080 my-spring-app # the app listens on 8080, reachable from outside on 9090
-e: environment variables
This is the main way to pass configuration into a container — database passwords, URLs of external services, Spring Boot profiles. Each variable is a separate -e flag:
docker run \
-e SPRING_PROFILES_ACTIVE=prod \
-e DB_URL=jdbc:postgresql://db:5432/myapp \
-e DB_PASSWORD=secret \
my-spring-app
Inside Spring Boot these variables are automatically picked up as application properties (through Environment).
-d: detached mode
By default docker run keeps the terminal busy — the output goes straight to the console. The -d (detached) flag runs the container in the background and returns control immediately:
docker run -d -p 8080:8080 my-spring-app
Docker prints the container id and that's it. You view logs separately — more on that below.
--name: container name
By default Docker makes up names like elegant_hopper — not very convenient. Set your own:
docker run -d --name my-app -p 8080:8080 my-spring-app
Now you can refer to the container by name in every command: docker logs my-app, docker stop my-app.
--rm: auto-remove
If you need the container only once (to run a test, execute a script), the --rm flag removes it automatically when it stops:
docker run --rm my-spring-app java -jar app.jar --check-config
Without --rm, stopped containers pile up and take up space.
Everything together — a typical run
docker run -d \
--name my-app \
--rm \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e DB_URL=jdbc:postgresql://db:5432/myapp \
my-spring-app:1.0
Passing configuration through environment variables
In the container world, configuration is not stored inside the image — it comes from outside at run time. This is an idea from the 12-factor app methodology: the same image is deployed across different environments (development, testing, production) with different variables.
For Spring Boot it looks like this: in application.yml you write ${DB_URL}, and you pass the actual value through -e DB_URL=... when you start the container. No secret ever ends up in the image.
If there are many variables, it is convenient to move them into a file:
# .env
SPRING_PROFILES_ACTIVE=prod
DB_URL=jdbc:postgresql://db:5432/myapp
DB_PASSWORD=secret
docker run -d --env-file .env -p 8080:8080 my-spring-app
Just don't commit a .env with real passwords into the repository.
The container lifecycle
After it starts, a container moves through several states.
Commands for managing it:
docker ps # list of running containers
docker ps -a # all of them, including stopped
docker stop my-app # graceful stop (SIGTERM, then SIGKILL after 10s)
docker start my-app # start a stopped container
docker rm my-app # remove a stopped container
docker rm -f my-app # stop and remove at once
docker stop sends the process a SIGTERM signal — the application can catch it and shut down cleanly (close database connections, wait for current requests). After 10 seconds Docker forcibly kills the process with SIGKILL.
Logs: docker logs
Everything the application writes to stdout and stderr is collected by Docker automatically.
docker logs my-app # print all accumulated logs
docker logs -f my-app # follow the logs in real time (like tail -f)
docker logs --tail 100 my-app # the last 100 lines
Spring Boot writes to stdout by default — you don't need to configure anything extra. If the application writes to a file inside the container, docker logs will not see that file.
Step inside: docker exec
Sometimes you need to see what is happening inside a running container — check the file system, environment variables, network connections.
docker exec -it my-app sh # start a shell inside the container
docker exec -it my-app bash # if bash is present in the image
The -it flags mean: -i — forward keyboard input, -t — attach a pseudo-terminal. Together they give you an interactive session.
Inside you can check the variables:
env | grep SPRING
Or look at which processes are running:
ps aux
docker exec does not restart the application — the command runs alongside the already-running process.
Healthcheck: verifying it works
Docker can periodically check whether a container is alive — not just that the process is running, but that the application actually responds. This is a healthcheck.
The easiest way is to define it in the Dockerfile:
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
After it starts, the container spends some time in the starting state — Docker waits for the healthcheck to pass for the first time. Then it moves to healthy or unhealthy.
To view the status:
docker inspect --format='{{.State.Health.Status}}' my-app
In an isolated docker run, a healthcheck only affects the status — the container is not restarted automatically. Automatic restart is already a job for Docker Compose or an orchestrator.
In short
docker runcreates a container from an image and starts it; when the process finishes, the container stops.-p host:container— port mapping; without it you cannot connect to the container from the outside.-e KEY=VALUE— pass an environment variable; this is the main way to configure things following the 12-factor principle.-d— detached mode;--name— a convenient name;--rm— remove the container after it stops.docker logs -f— follow the logs in real time;docker exec -it ... sh— an interactive session inside.docker stopsendsSIGTERM, giving the application 10 seconds to shut down cleanly.- A healthcheck verifies that the application is not just running but actually responding.
What to read next
- What Docker is and why you need it — if you haven't read it yet: images, layers, isolation from scratch.
- Volumes and data — how to keep data across container restarts.
- Networking in Docker — how containers talk to each other and to the outside world.