Skip to content

Docker Compose Lifecycle


Once you have a Compose file, the next question is simple: what happens to the application over time? Compose gives you commands to create, start, stop, restart, and remove the resources described by that file.

Understanding this lifecycle is important because up, stop, and down do different things. They are not interchangeable.

The Compose Lifecycle

Let’s use a small application with two services so the lifecycle is easier to see.

Create a directory:

mkdir compose-lifecycle
cd compose-lifecycle

Create compose.yaml:

compose.yaml
services:
  first:
    image: alpine
    command: ["sleep", "infinity"]

  second:
    image: alpine
    command: ["sleep", "infinity"]

Both services use the alpine image, but the containers stay running because their main process is sleep.

The application now looks like this:

Compose project
      │
      ├── first service
      │      └── container
      │
      └── second service
             └── container

docker compose up

Start the application:

docker compose up

Compose reads compose.yaml, creates the required containers, and starts them.

You’ll see Compose attach to the containers’ output.

Because the containers are running continuously, the command remains attached to them.

You can stop the attached output with:

Ctrl+C

This is an important distinction:

Stopping the command’s attached output is not the same thing as removing the application.

The containers can still exist after you leave the attached view.

You can start the application in the background with:

docker compose up -d

The -d option means Compose starts the application in detached mode.

Conceptually:

docker compose up
       │
       ▼
Read compose.yaml
       │
       ▼
Create required resources
       │
       ▼
Start services
       │
       ▼
Application is running

With -d, your terminal is returned to you while the application continues running.

docker compose stop

Now stop the running services:

docker compose stop

This stops the containers, but it does not remove them.

Think of stop as:

“Pause the running application, but keep its containers.”

The state becomes:

Compose project
      │
      ├── first service
      │      └── stopped container
      │
      └── second service
             └── stopped container

The containers still exist.

This is useful when you temporarily don’t need the application running but expect to start it again.

docker compose start

Because the containers still exist, you can start them again:

docker compose start

Compose starts the existing stopped containers.

The lifecycle now looks like:

up
 │
 ▼
running
 │
 ▼
stop
 │
 ▼
stopped
 │
 ▼
start
 │
 ▼
running

Notice what happened here.

stop did not destroy the containers.

start did not create new containers.

It simply started the existing ones again.

That’s the key difference between stop and down.

docker compose down

Now take the application down:

docker compose down

Unlike stop, down removes the containers created for the Compose application.

Conceptually:

running application
        │
        ▼
docker compose down
        │
        ▼
Compose resources removed

After down, the containers no longer exist.

So you cannot use:

docker compose start

to bring those same containers back.

Instead, use:

docker compose up -d

Compose will create what is needed from the configuration again.

Tip

docker compose down does not remove volumes if any are configured. This is meaningful because you want the volume as part of persistent storage, and Compose respects that. But if you want to remove volumes too, you obviously can:

docker compose down --volumes

stop vs down

This is one of the most important distinctions in Compose.

CommandContainersTypical purpose
docker compose upCreates if needed, then startsBring the application up
docker compose stopKeeps them, but stops themTemporarily stop
docker compose startStarts existing containersResume a stopped application
docker compose downRemoves Compose-managed containersTear the application down

A simple mental model is:

                 docker compose up
                         │
                         ▼
                    ┌─────────┐
                    │ RUNNING │
                    └────┬────┘
                         │
              docker compose stop
                         │
                         ▼
                    ┌─────────┐
                    │ STOPPED │
                    └────┬────┘
                         │
             docker compose start
                         │
                         └──────────────► RUNNING


                    RUNNING / STOPPED
                         │
                  docker compose down
                         │
                         ▼
                    CONTAINERS
                      REMOVED

The important idea is:

stop changes the running state. down removes the application resources.

What Happens When You Run up Again?

Suppose you have already run:

docker compose up -d

and the application is running.

If you run:

docker compose up -d

again, Compose does not simply create a second copy of every container.

It examines the current configuration and the resources associated with the project.

This is another benefit of declarative configuration:

You describe the desired application, and Compose works to bring the Docker resources into that state.

For a simple unchanged configuration, running up again does not mean “make another application.”

What Happens If You Change the Compose File?

Now make a small change.

Change the second service:

compose.yaml
services:
  first:
    image: alpine
    command: ["sleep", "infinity"]

  second:
    image: alpine
    command: ["sleep", "infinity"]
    environment:
      APP\_MODE: demo

Then run:

docker compose up -d

Compose notices that the service configuration has changed and updates the affected container as necessary.

This is an important reason to keep configuration in the Compose file.

You change the desired configuration, then ask Compose to bring the application to that state.

You don’t need to manually remember every Docker command required to recreate the changed resource.

up is more than “start”

A common beginner mistake is to think:

docker compose up

means exactly:

start existing containers

That’s not quite right.

up means roughly:

“Make the application described by this Compose file running.”

That can involve creating containers that don’t exist, starting existing containers, and recreating resources when their configuration needs to change.

By contrast:

docker compose start

has a much narrower meaning:

“Start existing stopped containers.”

This distinction becomes very useful as your Compose projects become more complex.

A Practical Lifecycle

Imagine you are developing an application.

In the morning, you bring it up:

docker compose up -d

You work with it for a while.

When you’re taking a break, you might stop it:

docker compose stop

Later, you resume:

docker compose start

At the end of the day, you might decide to completely remove the Compose application’s containers:

docker compose down

The next day:

docker compose up -d

Compose recreates what is necessary from compose.yaml.

This gives you a useful workflow:

    stateDiagram-v2
    direction LR

    [*] --> Absent

    Absent --> Running: up -d

    state Running {
        [*] --> RunningState
        RunningState --> RunningState: restart
    }

    Running --> Stopped: stop
    Stopped --> Running: start
    Stopped --> Running: up -d

    Running --> Paused: pause
    Paused --> Running: unpause

    Running --> Removed: down
    Stopped --> Removed: down

    Removed --> [*]
  
  • Absent → No container exists.
  • up -d / up → Create + start → Running.
  • RunningstopStopped.
  • Stoppedstart / up -d / upRunning.
  • RunningpausePausedunpauseRunning.
  • RunningrestartRunning.
  • Running / StoppeddownRemoved.
  • Removed → Container no longer exists.

Warning

One useful distinction using up after Stopped is that now Compose will start new container. If you use start than the same container is started. This is a useful distinction because you may lose data associated with the old container.

restart is different from stop + start

Compose also provides:

docker compose restart

This restarts the services.

Conceptually:

RUNNING
   │
   ▼
restart
   │
   ▼
RUNNING

The containers are not being intentionally removed as part of this operation.

You can think of it as useful when a service needs to be restarted but you don’t want to tear down the Compose application.

For example:

docker compose restart first

restarts only the first service.

Or:

docker compose restart

restarts the services in the Compose application.

The important point is that restart is about restarting existing services, while down is about tearing down the application’s resources.

up, start, stop, restart, and down

It helps to compare all five together:

CommandMain idea
docker compose upCreate/update as needed and start
docker compose startStart existing stopped containers
docker compose stopStop running containers without removing them
docker compose restartRestart existing services
docker compose downStop and remove the Compose application’s containers and associated default resources

The most important pair to remember is:

stop  → keep containers
down  → remove containers

And the most important distinction for starting is:

start → existing containers
up    → make the configured application running

What About Networks and Volumes?

This is where the lifecycle becomes especially interesting.

A Compose application can contain more than containers.

It can also define:

services
networks
volumes

Some of those resources have different lifecycle behavior.

For example, if you define a named volume for a database, you generally do not want a normal application shutdown to destroy the database’s data.

That is why understanding the difference between:

docker compose stop

and:

docker compose down

becomes important.

The exact behavior of networks and volumes will become much clearer when you define them explicitly in the upcoming lessons.

For now, remember that containers are not the only resources involved in a Compose application.

A Useful Mental Model

Think of the Compose lifecycle as three levels:

              CONFIGURATION
              compose.yaml
                    │
                    │ describes
                    ▼
               APPLICATION
                    │
          ┌─────────┴─────────┐
          │                   │
       running             stopped
          │                   │
          └─────────┬─────────┘
                    │
                   down
                    │
                    ▼
               RESOURCES
                 removed

The Compose file is the source of the desired configuration.

up brings the application into that desired running state.

stop temporarily changes the running state.

start resumes existing stopped containers.

restart restarts services.

down tears down the application resources managed by Compose.

Once you have this model in your head, the commands stop feeling like a collection of unrelated commands.

Hands-on Cleanup

If your example is still running, bring it down:

docker compose down

Then return to the parent directory:

cd ..

If you no longer need the example directory, remove it on Linux/macOS with:

rm -rf compose-lifecycle

Warning

rm -rf permanently removes the specified directory and its contents. Make sure you are in the correct parent directory and have the correct directory name before running it.

What You Should Remember?

The Compose lifecycle is mainly about understanding what happens to the resources behind your application:

docker compose up
    ↓
create/update + start
    ↓
running
    │
    ├── stop → stopped
    │             │
    │           start
    │             │
    │             └──→ running
    │
    ├── restart → running
    │
    └── down → resources removed

If you remember only two distinctions from this lesson, remember these:

stop keeps the containers; down removes them.

start starts existing containers; up makes the Compose application match its configuration and run.

In the next lesson, you’ll connect this lifecycle to one of the most important parts of a multi-container application: networking.

Last updated on