221

I want to start a service with docker-compose and keep the container running so I can get its IP-address via 'docker inspect'. However, the container always exits right after starting up.

I tried to add "command: ["sleep", "60"]" and other things to the docker-compose.yml but whenever I add the line with "command:..." I cant call "docker-compose up" as I will get the message "Cannot start container ..... System error: invalid character 'k' looking for beginning of value"

I also tried adding "CMD sleep 60" and whatnot to the Dockerfile itself but these commands do not seem to be executed.

Is there an easy way to keep the container alive or to fix one of my problems?

EDIT: Here is the Compose file I want to run:

version: '2'
services:
  my-test:
    image: ubuntu
    command: bash -c "while true; do echo hello; sleep 2; done"

It's working fine If I start this with docker-compose under OS X, but if I try the same under Ubuntu 16.04 it gives me above error message.

If I try the approach with the Dockerfile, the Dockerfile looks like this:

FROM ubuntu:latest
CMD ["sleep", "60"]

Which does not seem to do anything

EDIT 2: I have to correct myself, turned out it was the same problem with the Dockerfile and the docker-compose.yml: Each time I add either "CMD ..." to the Dockerfile OR add "command ..." to the compose file, I get above error with the invalid character. If I remove both commands, it works flawlessly.

Chris Snow
  • 23,813
  • 35
  • 144
  • 309
dingoglotz
  • 2,553
  • 3
  • 17
  • 21
  • 1
    Please include the docker-compose.yml, Dockerfile, and any scripts you're trying to debug. – BMitch Jul 23 '16 at 22:01
  • A related discussion, for those interested: [Development workflow for server and client using Docker Compose?](https://stackoverflow.com/q/31372886/320399) – blong Aug 19 '17 at 19:08

15 Answers15

265

To keep a container running when you start it with docker-compose, use the following command

command: tail -F anything

In the above command the last part anything should be included literally, and the assumption is that such a file is not present in the container, but with the -F option (capital -F not to be confused with -f which in contrast will terminate immediateley if the file is not found) the tail command will wait forever for the file anything to appear. A forever waiting process is basically what we need.

So your docker-compose.yml becomes

version: '2'
services:
  my-test:
    image: ubuntu
    command: tail -F anything

and you can run a shell to get into the container using the following command

docker exec -i -t composename_my-test_1 bash

where composename is the name that docker-compose prepends to your containers.

mit
  • 11,083
  • 11
  • 50
  • 74
Nick Settje
  • 3,022
  • 1
  • 11
  • 18
  • 1
    How do you stop the container when you are done with it? Is there a Ctrl+C, Ctrl+Z type of command? Right now I have to close the terminal to quit. – mac10688 Jan 18 '18 at 06:20
  • If you are inside of the container, you can type `exit` to get back to your host machine. If you are on your host, then you can stop the container using Docker (`docker stop composename_my-test_1`) or Docker Compose (`docker-compose stop`). – Nick Settje Jun 03 '18 at 04:09
  • This did not work for me, when I do `docker ps -a` the container status shows `Restarting (0) 5 seconds ago` and when I try `docker exec -it data-quality-test-db-sqlite sh` it says `Error response from daemon: Container [...] is restarting, wait until the container is running` – Alexis.Rolland Aug 13 '18 at 13:07
  • 2
    @Alexis.Rolland If you are willing to ask a new SO question and share some more details, then I'd be more than happy to take a look. My guess is that your error has something to do with the internals of one of your containers, as opposed to an issue with Docker or your host operating system. – Nick Settje Aug 21 '18 at 05:42
  • 1
    @mac10688 if there is no prompt in your attached container session then try ctrl-d to detach – Stark Jan 01 '19 at 21:45
  • 27
    `/dev/null` would be better in place for `anything` for tail command ref. https://stackoverflow.com/a/48732671/248616 – Nam G VU Oct 21 '19 at 09:24
  • As an alternative to `/dev/null`, you could replace `anything` with a log file that gives you insight into what the container is doing. While the literal `anything` will work, what I really meant is that `tail -F` for some file or stream is a good way to start a foreground process and keep a container running. – Nick Settje Oct 30 '19 at 01:17
  • You can also add the docker-compose line: `restart: unless-stopped` to help it know you want to distinguish between intentional `stop` and the command stopping. – Jesse Chisholm Feb 04 '20 at 00:38
  • I'm used to doing this using the `entrypoint` entry, but now I need to replace a single command with an entrypoint file and even though I still have `tail -f /dev/null` at the end of my `entrypoint.sh` file, the container still exits right away. Is there any way I can run the file and keep the container up? – Ariel Apr 06 '20 at 08:44
237

You can use tty configuration option.

version: '3'

services:
  app:
    image: node:8
    tty: true           # <-- This option

Note: If you use Dockerfile for image and CMD in Dockerfile, this option won't work; however, you can use the entrypoint option in the compose file which clears the CMD from the Dockerfile.

highjump
  • 2,655
  • 2
  • 10
  • 16
  • 21
    This works, and seems less hacky than `tail -f /dev/null`. With this, I'm able to run a containerized dev environment with a postgres database attached via `docker-compose up` and run a shell through another terminal using `docker exec`. – Psiloc Sep 19 '19 at 16:03
  • 1
    While currently NOT well documented. This is an official options that will achieve the same effect as `tail -f /dev/null` or `tail -f anything`, see here: https://docs.docker.com/compose/compose-file/ – b01 Sep 21 '19 at 11:56
  • 6
    @ABMRuman & Psiloc, It works only when you don't use "command" in the docker-compose.yml file. When using "command" you need another hack - hence the tail -F hack is perfectly suited here. – asafel Jan 29 '20 at 15:13
  • 1
    This must be the best answer if you use entrypoint in dockerfile. – DDKV587 Jun 12 '20 at 05:27
  • 2
    Doesn't work for me, the container still dies. It works if I add `tail -F /dev/null` in my entrypoint bash script, but it's weird to rely on that bash script being alive when all I want is to run some initial commands when the container is started. – XCS Jan 24 '21 at 16:23
  • Works for me, very clean. – progonkpa Oct 07 '21 at 14:01
87

Based on the comment of @aanand on GitHub Aug 26, 2015, one could use tail -f /dev/null in docker-compose to keep the container running.

docker-compose.yml example

version: '3'
services:
  some-app:
    command: tail -f /dev/null

Why this command?

The only reason for choosing this option was that it received a lot of thumbs up on GitHub, but the highest voted answer does not mean that it is the best answer. The second reason was a pragmatic one as issues had to be solved as soon as possible due to deadlines.

030
  • 10,842
  • 12
  • 78
  • 123
  • 4
    why exactly this command? What makes it better than others? In my case just starting a bash did the trick as well... – N4ppeL Jul 31 '19 at 09:54
  • @N4ppeL Good question. The only reason I choose this option was that it received a lot of thumbs up on github, but the highest voted answer does not mean that it is the best answer. The second reason was a pragmatic one as I had to solve the issue as soon as possible due to deadlines. – 030 Aug 01 '19 at 23:02
  • this overrides origin entrypoint, didnt execute origin entrypoint – david valentino Apr 04 '23 at 10:25
27
  • Create a file called docker-compose.yml
  • Add the following to the file
version: "3"

services:
  ubuntu:
    image: ubuntu:latest
    tty: true
  • Staying in the same directory, run docker-compose up -d from the terminal
  • Run docker ps to get the container id or name
  • You can run docker inspect $container_id
  • You can enter the container and get a bash shell running docker-compose exec ubuntu /bin/bash or docker-compose exec ubuntu /bin/sh
  • When done, make sure you are outside the container and run docker-compose down

Here's a small bash script (my-docker-shell.sh) to create the docker compose file, run the container, login to the container and then finally cleanup the docker container and the docker compose file when you log out.

#!/bin/bash

cat << 'EOF' > ./docker-compose.yml
---

version: "3"

services:
  ubuntu:
    image: ubuntu:latest
    command: /bin/bash
    # tty: true

...
EOF

printf "Now entering the container...\n"
docker-compose run ubuntu bash
docker-compose down

rm -v ./docker-compose.yml
GMaster
  • 1,431
  • 1
  • 16
  • 27
  • 1
    While this wasn't an actual answer to the question, I found it extremely valuable! I went further to use global bashrc functions for this approach: https://gist.github.com/loopmode/4d59a4e9a0a2ffacaec2dd14db4ae8bd – loopmode Aug 29 '19 at 11:14
  • It would however overwrite any existing `docker-compose.yml` file in the current directory; bit dangerous. – henrikstroem Mar 23 '23 at 11:18
16

I'm late to the party, but you can simply use: stdin_open: true

version: '2'
services:
  my-test:
    image: ubuntu
    stdin_open: true
Andreas
  • 8,694
  • 3
  • 14
  • 38
13

Some people here write about overwriting the entrypoint so that the command can also have its effect. But no one gives an example. I then:

docker-compose.yml:

version: '3'

services:

    etfwebapp:
        # For messed up volumes and `sudo docker cp`:
        command: "-f /dev/null"
        entrypoint: /usr/bin/tail
        tty: true

# ...

I am not sure if tty is needed at this point. Is it better to do it twice? In my case it did no harm and worked perfectly. Without entrypoint it didn't work for me because then command had no effect. So I guess for this solution tty is optional.

To understand which command is executed at start-up, simply read the entrypoint before the command (concat with space): /usr/bin/tail -f /dev/null.

qräbnö
  • 2,722
  • 27
  • 40
12

In the Dockerfile you can use the command:

{CMD sleep infinity}
GhostCat
  • 137,827
  • 25
  • 176
  • 248
John
  • 121
  • 1
  • 6
10

Blocking command is all you need.

I have been struggling with this problem for half a day. . There are many answers below, but not clear enough. And nobody said why.

In short, there are two methods, but it can also be said that there is only one, running a Blocking processes in background.


This first one is using COMMAND:

version: '3'
services:
  some-app:
    command: ["some block command"]

put some block command like sleep infinity, tail -f /dev/null, watch anything, while true ...

Here I recommend sleep infinity.


The second is enable tty=true, then open a shell in command like /bin/bash.

services:
  ubuntu:
    image: ubuntu:latest
    tty: true
    command: "/bin/bash"

Since the tty is enabled, bash will keep running background, you can put some other block commands before it if you want.

Be careful, you must excute shell command at the end, like

command: /bin/bash -c "/root/.init-service && /bin/bash"

As you can see, all you need is blocking command.

Jay
  • 738
  • 8
  • 14
5

Just a quick note

I have tested single image based on golang, so when I call docker-compose down here what I get:

version: "3.1"
...
command: tail -f /dev/null   # stopping container takes about 10 sec.
tty: true                    # stopping container takes about 2 sec.

My system info:

Ubuntu 18.04.4 LTS (64-bit)
Docker version 19.03.6, build 369ce74a3c
docker-compose version 1.26.0, build d4451659
blues911
  • 840
  • 8
  • 9
3

As the commenter stated, we'd have to see the Dockerfile in question to give you a complete answer, but this is a very common mistake. I can pretty much guarantee that the command you're trying to run is starting a background process. This might be the command you'd run in non-Docker situations, but it's the wrong thing to do in a Dockerfile. For instance, if what you're running is typically defined as a system service, you might use something like "systemctl start". That would start the process in the background, which will not work. You have to run the process in the foreground, so the entire process will block.

David M. Karr
  • 14,317
  • 20
  • 94
  • 199
0

I have used some of the solutions above which works for ubuntu or nodejs images. However they don't work with the bitnami/azure-cli image. Whatever specified in command becomes argument(s) for the az command of that image. After a bit of struggling, this is how the docker-compose.yml file should be:

version: "2.4"
services:
  azure-cli:
    image: "bitnami/azure-cli:latest"
    container_name: az-container
    entrypoint: tail -f /dev/null

As @highjump points out above, this entrypoint option overrides the internal command option, and keeps the container running.

CaTx
  • 1,421
  • 4
  • 21
  • 42
0

Add entrypoint to docker-compose file

entrypoint: ["tail", "-f", "/dev/null"]
Rayiez
  • 1,420
  • 19
  • 20
0

This was the only way I could get it to work without using something like tail -f /dev/null. I read many of the answers here saying that adding init to the compose file should be enough and confirmed the same in Docker's documentation but I could not get it to work with init alone.

# compose.yml
services:
  my-test:
    build:
      context: .
      dockerfile: dockerfile
    tty: true
    init: true
# dockerfile
FROM node:20-alpine
RUN apk add --no-cache  --update bash

CMD ["/bin/sh"]
Splendor
  • 1,386
  • 6
  • 28
  • 62
0

If you want a quick docker run option, in a tiny busybox image, as a one-liner:

docker run --rm --name forever busybox sh -c 'trap : TERM INT; tail -f /dev/null & wait'

If you want to exec into that container:

docker exec -it forever sh

You can stop it with CTRL + Z and it will stop gracefully and without delay.

If you prefer a docker-compose.yml file:

services:
  forever:
    image: busybox
    command: 'sh -c "trap : TERM INT; tail -f /dev/null & wait"'

Based on this.

lonix
  • 14,255
  • 23
  • 85
  • 176
-3

Okay I found my mistake. In the Dockerfile for the image used for compose I specified that the base image should be ubuntu:latest, but I previously created an image called ubuntu by myself and that image did not work. So I did not use the original ubuntu image but rather a corrupt version of my own image also called ubuntu.

dingoglotz
  • 2,553
  • 3
  • 17
  • 21