52

I need to run a docker container only if its not already running. Given this command. How would I run it only if it does not exists.

docker run --name nginx -d nginx

I am open to any script or language.

Marcel Lamothe
  • 12,452
  • 9
  • 34
  • 44
Luke101
  • 63,072
  • 85
  • 231
  • 359

5 Answers5

101

I would definitely suggest looking into docker-compose and docker-compose up as answered above.

Since your question is about docker run, i would simplify the answer of VonC like this

docker start nginx || docker run --name nginx -d nginx

If the container already is running, docker start will return 0 thus no docker run is executed. If the container EXISTS but is not running, docker start will start it, otherwise it docker run creates and starts it in one go.

The "exists but stopped" part is missing in VonC's answer.

Eugen Mayer
  • 8,942
  • 4
  • 33
  • 57
  • 5
    You might want to direct stderr of start to dev null so that if the container doesn't exist you don't get a load of error text in your script output i.e. `docker start nginx 2>/dev/null || docker run --name nginx -d nginx` – Brendan May 15 '20 at 20:03
34

Use a filter to check if a container of a certain name exists:
(See docker ps Filterring)

#!/bin/bash

name='nginx'

[[ $(docker ps -f "name=$name" --format '{{.Names}}') == $name ]] ||
docker run --name "$name" -d nginx

The docker run will only be executed if the first part is false.

To be on the safe side (docker ps might return several names), you might alternatively do (if you think the word "nginx" can't be part of any container name):

if ! docker ps --format '{{.Names}}' | grep -w nginx &> /dev/null; then
    docker run --name nginx -d nginx
fi

Or:

if ! docker ps --format '{{.Names}}' | egrep '^nginx$' &> /dev/null; then
    docker run --name nginx -d nginx
fi
x-yuri
  • 16,722
  • 15
  • 114
  • 161
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Since "the name filter matches on all or [part](https://docs.docker.com/engine/reference/commandline/ps/#name) of a container’s name", [`docker start || docker run`](https://stackoverflow.com/a/44739847/52499) seems like a more viable option. – x-yuri Jul 02 '19 at 09:25
  • Good point, but `docker ps -f name=...` might return more then one name (`nginx\ns1_nginx...` or something), so doing `if docker ps --format '{{.Names}}' | egrep '^nginx$' &> /dev/null; then ...; fi` seems to be a safer option. – x-yuri Jul 02 '19 at 10:55
  • @x-yuri yes, or `grep -w "nginx"` – VonC Jul 02 '19 at 10:57
  • @x-yuri Feel free to edit the answer and add safer alternatives, by the way: I (and any other reader) would appreciate it :) – VonC Jul 02 '19 at 10:58
7

Well if you are open to any language I recommend using docker-compose for this task. After installing it, create a file called docker-compose.yml with this content:

version: '2'
services:
  nginx:
    image: 'nginx' 

Then use:

docker-compose up -d

It will always check if the container is already running. If the container doesn't exists it will create it and run. If the container is stopped it just run it.

The best thing is if you alter the docker-compose.yml or pull a new version of the image it will automatically recreate the container preserving all volumes even the unnamed ones.

Regards

Carlos Rafael Ramirez
  • 5,984
  • 1
  • 29
  • 36
  • 1
    This does not appear to be the case if a different network is used. I created a network specifically for my CI server and ran a container with an alias. The same container/alias is referenced in my compose file and it complains about a conflict. Network is the only thing I can of it being. – Damien Roche Feb 03 '19 at 20:15
3

If the purpose is idempotency and the container in question is fungible (as is the case in your example), then the current state of the system (is/was it running) doesn't matter.

On a blank slate.

$ docker ps -a --format '{{.Names}}' | wc -l
0

Performing force remove and create when a container exists.

$ docker run --name=hello -d alpine sleep inf
0f6175804d69d48cdada66de57cb62c2855d1085c08a69f21e782c433be2c6a2
$ docker ps --format '{{.Names}}'
hello
$ docker rm -f hello ||:
hello
$ docker run --name=hello -d alpine sleep inf 
...

Is exactly the same as when it doesn't.

$ docker ps -a --format '{{.Names}}' | wc -l
0
$ docker rm -f hello 2>/dev/null ||:
$ docker run --name=hello -d alpine sleep inf
d0210fefc7e934fea6d669615538596cf372f15e9095edae81c35965ff021898

Is exactly the same when performing a conditional check.

$ docker ps -a --format '{{.Names}}'
hello
$ if ! docker ps -a --format '{{.Names}}' | grep hello >/dev/null ;then 
> docker run --name=hello -d alpine sleep inf 
> fi
$ docker rm -f hello
hello
$ if ! docker ps -a --format '{{.Names}}' | grep hello >/dev/null ;then 
> docker run --name=hello -d alpine sleep inf 
> fi
1edcbba0c01979edbe05072bd3c15b6ecfb129d9e20d45a33eff0921cc5f2ed3

Which makes force-remove/create an idempotent operation, where our guarantee of state, post operation, makes conditionals pointless and crufty.

If your container has to observe some underlying state, well, then forget all of this.

0

I know I'm late to the party, but this works fine for me:

docker ps | grep nginx >/dev/null || docker run nginx