Troubles with postgres while building docker container

I am trying to lauch DJANGO + POSTGRES inside a single container with DOCKERFILE. I have the folowing dockerfile which basicly installs all the python dependencies and postgresql. Then it lauches the script to create db and user (presented below).

FROM python

WORKDIR /app

COPY requirements.txt .

RUN pip install -r requirements.txt

COPY . .

RUN apt update && apt -y install postgresql postgresql-contrib 

RUN service postgresql start && su -c "bash config.db.bash" postgres

WORKDIR /app/psql

RUN python manage.py makemigrations && python manage.py migrate #Error

EXPOSE 8000

CMD service postgresql start && python manage.py runserver 0.0.0.0:8000

Here is the script "config.db.bash"

#!/bin/bash

psql <<-INPUT
    CREATE USER docker;
    ALTER USER docker WITH PASSWORD 'password';
    CREATE DATABASE docker;
    GRANT ALL PRIVILEGES ON DATABASE docker TO docker
INPUT

requirements.txt

asgiref==3.7.2
Django==5.0.2
sqlparse==0.4.4
tzdata==2024.1
psycopg2

Here is the DJANGO`s settings.py -> Database config (psycopg_2 module installed)

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "docker",
        "USER": "docker",
        "PASSWORD": "password",
        "HOST": "localhost",
        "PORT": 5432,
    }
}

It works just fine until this line

RUN python manage.py makemigrations && python manage.py migrate

Then docker says that error occured during this operation. Log basicly says that DJANGO cant connect to postgress DB.

> [9/9] RUN python manage.py makemigrations && python manage.py migrate:
3.377 /usr/local/lib/python3.12/site-packages/django/core/management/commands/makemigrations.py:160: RuntimeWarning: Got an error checking a consistent migration history performed for database connection 'default': connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused  
3.377   Is the server running on that host and accepting TCP/IP connections?
3.377 connection to server at "localhost" (::1), port 5432 failed: Cannot assign requested address
3.377   Is the server running on that host and accepting TCP/IP connections?
3.377
3.377   warnings.warn(
3.384 No changes detected
3.748 Traceback (most recent call last):
3.748   File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 275, in ensure_connection
3.748     self.connect()
3.748   File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.748     return func(*args, **kwargs)
3.749            ^^^^^^^^^^^^^^^^^^^^^
3.749   File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 256, in connect
3.749     self.connection = self.get_new_connection(conn_params)
3.749                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.749   File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.749     return func(*args, **kwargs)
3.750            ^^^^^^^^^^^^^^^^^^^^^
3.750   File "/usr/local/lib/python3.12/site-packages/django/db/backends/postgresql/base.py", line 277, in get_new_connection
3.750     connection = self.Database.connect(**conn_params)
3.750                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.750   File "/usr/local/lib/python3.12/site-packages/psycopg2/__init__.py", line 122, in connect
3.751     conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
3.751            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.751 psycopg2.OperationalError: connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused
3.751   Is the server running on that host and accepting TCP/IP connections?
3.751 connection to server at "localhost" (::1), port 5432 failed: Cannot assign requested address
3.751   Is the server running on that host and accepting TCP/IP connections?
3.751
3.751
3.751 The above exception was the direct cause of the following exception:
3.751
3.751 Traceback (most recent call last):
3.751   File "/app/psql/manage.py", line 22, in <module>
3.751     main()
3.751   File "/app/psql/manage.py", line 18, in main
3.751     execute_from_command_line(sys.argv)
3.751   File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
3.752     utility.execute()
3.752   File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 436, in execute
3.752     self.fetch_command(subcommand).run_from_argv(self.argv)
3.752   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 413, in run_from_argv
3.752     self.execute(*args, **cmd_options)
3.752   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 459, in execute
3.753     output = self.handle(*args, **options)
3.753              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.753   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 107, in wrapper
3.753     res = handle_func(*args, **kwargs)
3.753           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.753   File "/usr/local/lib/python3.12/site-packages/django/core/management/commands/migrate.py", line 100, in handle
3.754     self.check(databases=[database])
3.754   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 486, in check
3.754     all_issues = checks.run_checks(
3.754                  ^^^^^^^^^^^^^^^^^^
3.754   File "/usr/local/lib/python3.12/site-packages/django/core/checks/registry.py", line 88, in run_checks
3.754     new_errors = check(app_configs=app_configs, databases=databases)
3.754                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.754   File "/usr/local/lib/python3.12/site-packages/django/core/checks/model_checks.py", line 36, in check_all_models
3.755     errors.extend(model.check(**kwargs))
3.755                   ^^^^^^^^^^^^^^^^^^^^^
3.755   File "/usr/local/lib/python3.12/site-packages/django/db/models/base.py", line 1610, in check
3.756     *cls._check_constraints(databases),
3.756      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.756   File "/usr/local/lib/python3.12/site-packages/django/db/models/base.py", line 2460, in _check_constraints
3.757     connection.features.supports_nulls_distinct_unique_constraints
3.757   File "/usr/local/lib/python3.12/site-packages/django/utils/functional.py", line 47, in __get__
3.757     res = instance.__dict__[self.name] = self.func(instance)
3.757                                          ^^^^^^^^^^^^^^^^^^^
3.757   File "/usr/local/lib/python3.12/site-packages/django/db/backends/postgresql/features.py", line 141, in is_postgresql_15
3.757     return self.connection.pg_version >= 150000
3.757            ^^^^^^^^^^^^^^^^^^^^^^^^^^
3.757   File "/usr/local/lib/python3.12/site-packages/django/utils/functional.py", line 47, in __get__
3.757     res = instance.__dict__[self.name] = self.func(instance)
3.757                                          ^^^^^^^^^^^^^^^^^^^
3.757   File "/usr/local/lib/python3.12/site-packages/django/db/backends/postgresql/base.py", line 438, in pg_version
3.758     with self.temporary_connection():
3.758   File "/usr/local/lib/python3.12/contextlib.py", line 137, in __enter__
3.758     return next(self.gen)
3.758            ^^^^^^^^^^^^^^
3.758   File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 691, in temporary_connection
3.758     with self.cursor() as cursor:
3.758          ^^^^^^^^^^^^^
3.758   File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.759     return func(*args, **kwargs)
3.759            ^^^^^^^^^^^^^^^^^^^^^
3.759   File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 316, in cursor
3.759     return self._cursor()
3.759            ^^^^^^^^^^^^^^
3.759   File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 292, in _cursor
3.759     self.ensure_connection()
3.760   File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.760     return func(*args, **kwargs)
3.760            ^^^^^^^^^^^^^^^^^^^^^
3.760   File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 274, in ensure_connection
3.760     with self.wrap_database_errors:
3.760   File "/usr/local/lib/python3.12/site-packages/django/db/utils.py", line 91, in __exit__
3.760     raise dj_exc_value.with_traceback(traceback) from exc_value
3.760   File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 275, in ensure_connection
3.761     self.connect()
3.761   File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.761     return func(*args, **kwargs)
3.761            ^^^^^^^^^^^^^^^^^^^^^
3.761   File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 256, in connect
3.761     self.connection = self.get_new_connection(conn_params)
3.761                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.761   File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.761     return func(*args, **kwargs)
3.762            ^^^^^^^^^^^^^^^^^^^^^
3.762   File "/usr/local/lib/python3.12/site-packages/django/db/backends/postgresql/base.py", line 277, in get_new_connection
3.762     connection = self.Database.connect(**conn_params)
3.762                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.762   File "/usr/local/lib/python3.12/site-packages/psycopg2/__init__.py", line 122, in connect
3.762     conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
3.763            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.763 django.db.utils.OperationalError: connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused
3.763   Is the server running on that host and accepting TCP/IP connections?
3.763 connection to server at "localhost" (::1), port 5432 failed: Cannot assign requested address
3.763   Is the server running on that host and accepting TCP/IP connections?
3.763
------
Dockerfile:17
--------------------
  15 |     WORKDIR /app/psql
  16 |
  17 | >>> RUN python manage.py makemigrations && python manage.py migrate
  18 |
  19 |     EXPOSE 8000

Before looking for help I tried several ways to solve this problem by myself.

  1. I tried to start/restart psql server or start it again with the commands

    RUN service postgresql start
    
    RUN service postgresql restart 
    

    Server tried to start/restart and after long time of waiting it throwed timeout error.

  2. I tried to modify DJANGO settings.py -> Database config. Changed "localhost" to "0.0.0.0" and to "127.0.0.1". Then I tried to specify PORT as a string. -> Didnt work.

  3. I tried to run

    RUN service postgresql status

    It said that after this command in dockerfile...

    RUN service postgresql start && su -c "bash config.db.bash" postgres
    

    ... server went ofline somehow! And as I mentioned before when I was trying to start it again timeout error occured!

Im looking for solution using DOCKERFILE and not Docker-Compose. Also I dont want to use DIFFERENT BASE IMAGE in DOCKERFILE.

Let me start early: The Dockerfile is used to build the container, meaning installing everything, setting up directory structure etc. The Dockerfile is not supposed to run services already. This you typically want to have when you start the container. Every intermediate command in your Dockerfiles requires a "start and end", so does the RUN command at that place. But how can the database run alongside successfully in a container that is not even fully build yet?

Knowing that the database can not run where you are trying it to run it is easy to figure out why the migration command fails. You are attempting to connect to a database that is not currently running.

So how to mitigate? Disclaimer: What I am telling you now is not best practice at all. You typically want to run one process per container. One container for one task. This slowly goes to the direction to docker compose. Anyways to answer your question:

You would want to run a processmanager like supervisord. Remember, one container one main process. In this case this main process would be supervisord (which handles then your postgres and development server).

Meaning at the end of your Dockerfile you want to start an entrypoint script. This has to

  1. Start up the database
  2. Do the migrations
  3. Start the development server.

This is a lot to set up. But be considerative about the work you put in. Because even after setting it up as I said it is not a good solution. When the container dies, your database is gone. You need to setup a volume, somewhere. The development server is called development server for a reason. Meaning when you move to production you need a third service aswell (nginx for example to handle your staticfiles). This would introduce even a third service.

I do not know your reasoning behind the "all in one container approach" and I do not recommend it but the answer to your question is a service like supervisord.

Docker docs to this topic

Back to Top