56

I want to deploy my rails project using Docker. So I use Docker-Compose. But I get one weird error message. When run docker-compose up(this contains db-container with postgresql, redis and web container with rails) I get a

web_1 | => Booting Puma web_1 | => Rails 4.2.4 application starting in production on http://0.0.0.0:3000 web_1 | => Runrails server -hfor more startup options web_1 | => Ctrl-C to shutdown server web_1 | A server is already running. Check /usr/src/app/tmp/pids/server.pid. web_1 | Exiting So I cannot understand why do I get this message, because every time I run docker-compose up, new containers start, not the previous. Even if want to delete these server.pid I am not able to do that, because this container isn't running.

my docker-compose.yml file

web:
  dockerfile: Dockerfile-rails
  build: .
  command: bundle exec rails s -p 3000 -b '0.0.0.0'
  ports:
    - "80:3000"
  links:
    - redis
    - db
  environment:
    - REDISTOGO_URL=redis://user@redis:6379/

redis:
  image: redis

db:
  dockerfile: Dockerfile-db
  build: .
  env_file: .env_db

Dockerfile-rails

FROM rails:onbuild

ENV RAILS_ENV=production

I don't think I need to post all my Dockerfiles

UPD: I fixed it by myself: i just deleted all my containers and ran the docker-compose up once again

handkock
  • 1,067
  • 2
  • 11
  • 23

10 Answers10

76

You are using an onbuild image, so your working direcotry is mounted in the container image. This is very good for developing, since your app is updated in realtime when you edit your code, and your host system gets updated for example when you run a migration.

This also means that your host system tmp directory will be written with the pid file every time a server is running and will remain there if the server is not shut down correctly.

Just run this command from your host system:

sudo rm tmp/pids/server.pid 

This can be a real pain when you are for example using foreman under docker-compose, since just pressing ctrl+c will not remove the pid file.

TopperH
  • 2,183
  • 18
  • 33
  • 6
    The problem is: there's no `server.pid` file in my `tmp/pids` directory – handkock Jan 26 '16 at 20:24
  • 8
    I think a workaround could be to run "docker-compose run web /bin/bash" and remove the pid file from there. Then try to investigate how it got there on the first place. Also you should run "docker inspect" on your container since it's not probably mounting your host directory correctly. – TopperH Jan 26 '16 at 20:29
  • 3
    A variation of this worked for me, where I added to my startup script `rm /myapp/tmp/pids/server.pid` before running foreman start. – Ben Morris Apr 27 '17 at 01:18
  • I had to go into the container `docker-compose run web /bin/bash` and then remove the pid. – Steven Aguilar Jul 13 '18 at 18:36
  • 1
    @StevenAguilar this actually means you are not using an onbuild image as the OP was. In your case it would have actually been a better practice to just `docker-compose rm web`, since there is nothing persistent in that container – TopperH Jul 14 '18 at 12:45
  • @TopperH that didn't work, I still kept the error. When I went back to the server.pid I got `Unable to find image 'web:latest' locally` since I had removed it. – Steven Aguilar Jul 18 '18 at 15:59
  • @StevenAguilar are you running your rails server using `docker-compose up` ? – TopperH Jul 18 '18 at 18:42
  • 1
    @TopperH yes, I'm using `docker-compose up` to start the rails server – Steven Aguilar Jul 18 '18 at 18:44
46

I was stumped by the same problem for a bit until I figured out what was really going on. The real answer is further down... At first, you may want to try something like the following command in your docker-compose.yml file:

command: /bin/sh -c "rm -f /rails/tmp/pids/server.pid && rails server puma"

(I'm using Alpine and busybox to keep things tiny, so no bash! But the -c will work with bash too) This will delete the file if it exists so that you don't get stuck with the problem that the container keeps exiting and sitting there unable to run commands.

Unfortunately, that is NOT a good solution because you add an extra layer of /bin/sh ahead of the server and that prevents the server from getting the stop command. The result is that the docker stop commands won't give a graceful exit and the problem will always happen.

You could just run the rm command using docker-compose up to remove the file and then change the command back to the server and carry on.

However, the real answer is the create a simple docker-entry.sh file and make sure you call it using the exec form Entrypoint Documentation so that signals (like stop) get to the server process.

#!/bin/sh
set -e

if [ -f tmp/pids/server.pid ]; then
  rm tmp/pids/server.pid
fi

exec bundle exec "$@"

NOTE: we use exec on the last line to make sure that rails will be run as pid 1 (i.e. no extra shell) and thus get the signals to stop. And then in the Dockerfile (or the compose.yml) file add the entrypoint and command

# Get stuff running
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["rails", "server", "puma"]

And again, you MUST use the [] format so that it is exec'd instead of run as sh -c ""

Brendon Whateley
  • 1,302
  • 13
  • 22
  • 5
    Great answer. I had to set my entrypoint to "/myapp/docker-entrypoint.sh" to get this to work. Related discussion at https://github.com/docker/compose/issues/1393 . – Joao Costa Nov 05 '16 at 18:47
  • 8
    Fantastic answer, thank you. Note that I had to `chmod +x docker-entrypoint.sh` to resolve a permission denied error. – Dan Kohn Jun 26 '17 at 03:06
  • 1
    Could you give an example of the entrypoint/command for docker-compose? – Seafish Sep 18 '17 at 01:08
  • This is the preferred solution – Natus Drew Jun 09 '18 at 17:59
  • actually i would do "rm -f /rails/tmp/pids/server.pid; rails s " rather than && as it will return exit 1 if the file does not exist. – mirageglobe Nov 13 '18 at 20:27
  • As I mentioned at the beginning, the solution using `&&` is a hack. If that is what you want, you can replace the `&&` with a `;`. But a better solution is the modify the if statement in the suggested `docker-entrypoint.sh`. Consider however that either solution will make it impossible to start the container if it was shut down cleanly, since that will remove the PID file resulting in an error you desire. – Brendon Whateley Nov 14 '18 at 19:53
  • 1
    As @dankohn suggested, run `chmod +x docker-entrypoint.sh`, but do this locally, before building the image! I wasted an hour or more trying to run this command in my Dockerfile - which failed hard – davegson Dec 04 '18 at 14:10
  • 2
    @Seafish, I added [an adapted answer](https://stackoverflow.com/a/53615194/2235594) to solve this issue when using docker-compose – davegson Dec 04 '18 at 14:34
  • Docker guide states the same https://docs.docker.com/compose/rails/. – Artur INTECH Dec 27 '20 at 16:45
24

This is an adapted version of Brendon Whateleys answer for

Docker Compose Solution

1. Create docker-entrypoint.sh

#!/bin/bash
set -e

if [ -f tmp/pids/server.pid ]; then
  rm tmp/pids/server.pid
fi

exec bundle exec "$@"

2. Adapt your docker-compose.yml

services:
  web:
    build: .
    entrypoint: /myapp/docker-entrypoint.sh
    command: ["rails", "server", "-b", "0.0.0.0"]
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"

Notice that you will have to provide the path to where you mounted your app, iE: /myapp

2.5 If you encounter a permission error

Run this in your terminal before running docker-compose up or building your image. Thanks sajadghawami.

chmod +x docker-entrypoint.sh

3. Enjoy never having to remove server.pid manually again

davegson
  • 8,205
  • 4
  • 51
  • 71
7

for my work:

docker-compose run web bash

and then go folder by folder with the command cd (i am using win 7 toolbox) so in the end i use in the bash :

rm tmp/pids/server.pid

mrpepo877
  • 490
  • 6
  • 23
5

What I have done, Is to go on the bash shell of docker:

docker-compose run web /bin/bash

then remove the following file

rm tmp/pids/server.pid

Hope that it help!

charlesdg
  • 814
  • 11
  • 18
5

If your solution is using rm to delete the pid file on an entry point, I think a better solution is to tell rails to write the pid file to an ephemeral path, like:

web:
   command: bundle exec rails server -b '0.0.0.0' -p 3000 -P /tmp/rails.pid 

However, most of the time it's caused because a docker container is still running, what I use to do is:

$ docker stop $(docker ps -aq)

That will stop all running containers. This -a for all -q for quiet (only) container ids

Arnold Roa
  • 7,335
  • 5
  • 50
  • 69
3

For ones, who have problem when there is no tmp/pids/server.pid actually exists:

(Docker version 1.11.2, build b9f10c9)

  1. Solution, which worked for me is to add exact command to docker-compose.yml, for example:

    command:
       rails s -b0
    

    Having this command, which in fact may simply duplicate Dockerfile's CMD - issue with stale .pid do not appear.

  2. Other option, when you need to use Dockerfile's CMD, is to run with option --build to rebuild image:

    docker-compose up --build
    

    Though it takes time, so first solution is more convenient

Daniel Garmoshka
  • 5,849
  • 39
  • 40
0

If you are feeling adventurous feel free to search for server.pid in the codebase and check where the pid is getting written into. That way you can check if not allowing it to write fixes the root problem for you. Because seemingly the pid creation helps prevent you from running duplicate services in two different docker containers. But that should be taken care of in docker as it is. If for some reason, it does not, the port collision methodology should definitely take care of that and alert you. Note: I have not tried this approach as yet as that is moving the system in general:)

Alternatively, you could have commands in your Makefile. Where the task does this:

task:
docker-compose stop service_name
docker-compose rm service_name
rm -f ./tmp/pids/server.pid (or relative pathname of your pid file)
docker-compose up service_name

And then vrooom make task in terminal and hit enter. The above task commands assume the service you want to run are in a file named docker-compose.yml

If the name is different then the command

docker-compose stop service_name --> docker-compose -f your-compose-filename.yml stop service_name

so on and so forth.

==================== Came back to update this. So, I did try commenting out my write_pid method functionality. Basically keep the method definition, but not have it do anything. It is working well so far. It does not write the pid at all. If you are running your services in docker, I believe this is completely safe.

Sheetal Kaul
  • 3,029
  • 2
  • 13
  • 6
  • This looks like it is for the case where the PID file is outside the container, which wasn't the case in the original question. Keeping the PID outside the container doesn't make any sense because the PID is only unique inside a container, meaning it can't reliably do anything across containers. – Brendon Whateley Dec 04 '18 at 21:12
0

In your docker-compose.yml file, under the application container itself, you can either use:

command: ["rm /your-app-path/tmp/pids/server.pid && bundle exec bin/rails s -p 3000 -b '0.0.0.0'"]

or

command: ["rm /your-app-path/tmp/pids/server.pid; foreman start"]

the reason for either ";" or "&&" is that the latter will send an exit signal if rm fails to find the file, forcing your container to prematurely stop. the prior will continue to execute.

why is this caused in the first place? the rational is that if the server (puma/thin/whatever) does not cleanly exit, it will leave a pid in the host machine causing an exit error.

mirageglobe
  • 2,446
  • 2
  • 24
  • 30
0

I had this problem when trying to run docker on a group of containers.

The problem was the directory from which I was deploying the container had had a server running that was killed but which didn't remove the tmp/pids/server.pid file at the time it was killed. So every time I deployed, it was there.

So, if that's not your problem, @Brendon Whatley's and @davegson's answers might be necessary. But you may be able to save yourself some pain if it is.

Derrell Durrett
  • 544
  • 10
  • 26