Docker Compose: Rails won't respect the DATABASE_URL to connect to Postgres

Viewed 5559

I'm trying to containerize my Ruby on Rails 5.1.0 application, but I'm having some trouble with it not picking up DATABASE_URL from the environment. In docker-compose.yml, I have the following service:

app:
  build: .
  command: bundle exec rails s -p 3000 -b '0.0.0.0'
  volumes:
    - .:/myapp
  environment:
    DATABASE_URL: postgres://postgres:pw@db:5432/myapp_development

The environment gets picked up just fine if I run docker-compose run app rails c:

$ docker-compose run app rails c
Running via Spring preloader in process 25
Loading development environment (Rails 5.1.0)
irb(main):001:0> ENV['DATABASE_URL']
=> "postgres://postgres:pw@db:5432/myapp_development"

But then if I run docker-compose run app rake db:create, I get an error about not being able to connect to localhost:

$ docker-compose run app rake db:create
Database 'myapp_development' already exists
could not connect to server: Connection refused
    Is the server running on host "localhost" (::1) and accepting
    TCP/IP connections on port 5432?
could not connect to server: Connection refused
    Is the server running on host "localhost" (127.0.0.1) and accepting
    TCP/IP connections on port 5432?
Couldn't create database for {"adapter"=>"postgresql", "host"=>"localhost", "pool"=>10, "timeout"=>5000, "database"=>"myapp_test"}
rake aborted!

Any idea what I'm doing wrong here?

Edit: Here's what database.yml looks like:

common: &common
  adapter: postgresql
  pool: 10
  timeout: 5000

development:
  <<: *common
  database: myapp_development

test:
  <<: *common
  database: myapp_test

production:
  <<: *common
  database: myapp_production
4 Answers

I also had a similar issue, the causes of which were multi-fold.

TL;DR, make sure that:

  1. Your app container is correctly linked to and dependent on your postgres container
  2. Your DATABASE_URL references the correct hostname of your postgres container
  3. The RAILS_ENV ENV variable is explicitly set within your app container

First off, I had to make sure my app container in docker_compose.yml was linked to and depended on the postgres container:

version: '3'
    services:
      app:
        container_name: 'app'
        depends_on:
          - postgres
        links:
          - postgres

Second, the DATABASE_URL ENV variable I was providing database.yml did not reference the correct hostname of the postgres container. Originally, mine looked like:

'postgres://postgres:@localhost:5432/my_app_development'

I changed it to:

'postgres://postgres:@postgres:5432/my_app_development'

Not an apparent issue in your case, but worth noting for others.

Third, I wasn't explicitly specifying the RAILS_ENV environment. I set that ENV variable to development in docker_compose.yml so that the correct database.yml configuration was used.

The end result of my database.yml file:

development: &default
  adapter: postgresql
  encoding: unicode
  database: my_app_development
  username: postgres
  pool: 5
  timeout: 5000
  url: <%= ENV['DATABASE_URL'] %>

test:
  <<: *default
  database: my_app_test
  pool: 5
  url: <%= ENV['DATABASE_URL'] %>

production:
  <<: *default
  url: <%= ENV['DATABASE_URL'] %>

The end result of my docker_compose.yml file:

version: '3'
services:
  app:
    container_name: 'app'
    environment:
      DATABASE_URL: 'postgres://postgres:@postgres:5432/my_app_development'
      RAILS_ENV: 'development'
    build: .
    ports:
      - '3000:3000'
    volumes:
      - ./:/dev
    depends_on:
      - postgres
    links:
      - postgres
    tty: true

  postgres:
    container_name: 'postgres'
    image: postgres:9.5
    ports:
      - '5432:5432'
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD:

After that, all bundle exec rake db: commands worked just fine.

Hope that helps!

I think @Зелёныйenter's answer is mostly correct, the problem is that the rake task is trying to create databases for development and test.

The line

Database 'myapp_development' already exists

is probably there because the DB was created in a previous execution of the same command, that created the DB and also failed in trying to create the test DB.

One would think that by specifying the DB name in the URL, in this case myapp_development

DATABASE_URL: postgres://postgres:pw@db:5432/myapp_development

that would be the only DB to create, but it is not. I used DATABASE_URL without specifying the DB name, like:

DATABASE_URL: postgres://postgres:pw@db:5432

with the same result, one created, one failed.

As I understand, however, this is not a problem of RAILS_ENV definition. This is expected behaviour, according to this comment on an issue about this very subject, which by the way, is still in discussion.

So far, there are 3 suggested workarounds: two are gems, and the other is the modification or elimination of database.yml.

One possible database.yml that works is:

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  url: <%= ENV['DATABASE_URL'] %>

development:
  <<: *default
  database: app_development

test:
  <<: *default
  database: app_test

Which will create both databases without error.

Related