According to this tutorial I switched to postgresql code.
# django_project/settings.py
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "postgres",
"USER": "postgres",
"PASSWORD": "postgres",
"HOST": "db", # set in docker-compose.yml
"PORT": 5432, # default postgres port
}
}
Here is docker-compose.yml
# docker-compose.yml
version: "3.9"
services:
web:
build: .
command: python /code/manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- 8000:8000
depends_on:
- db
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- "POSTGRES_HOST_AUTH_METHOD=trust"
volumes:
postgres_data:
Everything worked before editing DATABASES
section.
When I run
$ curl 127.0.0.1:8000
curl: (56) Recv failure: Connection reset by peer
$ sudo docker-compose logs
ch3-postresql-web-1 | Watching for file changes with StatReloader
ch3-postresql-web-1 | Exception in thread django-main-thread:
ch3-postresql-web-1 | Traceback (most recent call last):
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 244, in ensure_connection
ch3-postresql-web-1 | self.connect()
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner
ch3-postresql-web-1 | return func(*args, **kwargs)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 225, in connect
ch3-postresql-web-1 | self.connection = self.get_new_connection(conn_params)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner
ch3-postresql-web-1 | return func(*args, **kwargs)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/backends/postgresql/base.py", line 203, in get_new_connection
ch3-postresql-web-1 | connection = Database.connect(**conn_params)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/psycopg2/__init__.py", line 122, in connect
ch3-postresql-web-1 | conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
ch3-postresql-web-1 | psycopg2.OperationalError: connection to server at "db" (172.19.0.2), port 5432 failed: Connection timed out
ch3-postresql-web-1 | Is the server running on that host and accepting TCP/IP connections?
ch3-postresql-web-1 |
ch3-postresql-web-1 |
ch3-postresql-web-1 |
ch3-postresql-web-1 |
ch3-postresql-web-1 | The above exception was the direct cause of the following exception:
ch3-postresql-web-1 |
ch3-postresql-web-1 |
ch3-postresql-web-1 | Traceback (most recent call last):
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/threading.py", line 1009, in _bootstrap_inner
ch3-postresql-web-1 | self.run()
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/threading.py", line 946, in run
ch3-postresql-web-1 | self._target(*self._args, **self._kwargs)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/utils/autoreload.py", line 64, in wrapper
ch3-postresql-web-1 | fn(*args, **kwargs)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/core/management/commands/runserver.py", line 137, in inner_run
ch3-postresql-web-1 | self.check_migrations()
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 576, in check_migrations
ch3-postresql-web-1 | executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/migrations/executor.py", line 18, in __init__
ch3-postresql-web-1 | self.loader = MigrationLoader(self.connection)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/migrations/loader.py", line 58, in __init__
ch3-postresql-web-1 | self.build_graph()
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/migrations/loader.py", line 235, in build_graph
ch3-postresql-web-1 | self.applied_migrations = recorder.applied_migrations()
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/migrations/recorder.py", line 81, in applied_migrations
ch3-postresql-web-1 | if self.has_table():
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/migrations/recorder.py", line 57, in has_table
ch3-postresql-web-1 | with self.connection.cursor() as cursor:
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner
ch3-postresql-web-1 | return func(*args, **kwargs)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 284, in cursor
ch3-postresql-web-1 | return self._cursor()
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 260, in _cursor
ch3-postresql-web-1 | self.ensure_connection()
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner
ch3-postresql-web-1 | return func(*args, **kwargs)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 243, in ensure_connection
ch3-postresql-web-1 | with self.wrap_database_errors:
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/utils.py", line 91, in __exit__
ch3-postresql-web-1 | raise dj_exc_value.with_traceback(traceback) from exc_value
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 244, in ensure_connection
ch3-postresql-web-1 | self.connect()
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner
ch3-postresql-web-1 | return func(*args, **kwargs)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 225, in connect
ch3-postresql-web-1 | self.connection = self.get_new_connection(conn_params)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner
ch3-postresql-web-1 | return func(*args, **kwargs)
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/django/db/backends/postgresql/base.py", line 203, in get_new_connection
ch3-postresql-web-1 | connection = Database.connect(**conn_params)
ch3-postresql-db-1 |
ch3-postresql-db-1 |
ch3-postresql-web-1 | File "/usr/local/lib/python3.10/site-packages/psycopg2/__init__.py", line 122, in connect
ch3-postresql-web-1 | conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
ch3-postresql-web-1 | django.db.utils.OperationalError: connection to server at "db" (172.19.0.2), port 5432 failed: Connection timed out
ch3-postresql-web-1 | Is the server running on that host and accepting TCP/IP connections?
ch3-postresql-web-1 |
ch3-postresql-web-1 |
ch3-postresql-db-1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
ch3-postresql-db-1 |
ch3-postresql-db-1 |
ch3-postresql-db-1 | 2025-01-09 12:30:20.250 UTC [1] LOG: starting PostgreSQL 13.18 (Debian 13.18-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
ch3-postresql-db-1 | 2025-01-09 12:30:20.251 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
ch3-postresql-db-1 | 2025-01-09 12:30:20.251 UTC [1] LOG: listening on IPv6 address "::", port 5432
ch3-postresql-db-1 | 2025-01-09 12:30:20.257 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
ch3-postresql-db-1 | 2025-01-09 12:30:20.265 UTC [26] LOG: database system was shut down at 2025-01-09 12:30:10 UTC
ch3-postresql-db-1 | 2025-01-09 12:30:20.284 UTC [1] LOG: database system is ready to accept connections
The failure to connect from web
happens before db
reports that it's ready to accept connections; depend_on: db
only forces web
to wait for the whole db
container to start, not for Postgres inside the container to start. Your web
is likely calling db
before it's ready to answer.
The docker compose
manual suggests it's best to make depend_on
check the service status:
# docker-compose.yml
version: "3.9"
services:
web:
build: .
command: python /code/manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- 8000:8000
depends_on:
db:
condition: service_healthy
db:
image: postgres:13
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
retries: 5
start_period: 30s
timeout: 10s
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- "POSTGRES_HOST_AUTH_METHOD=trust"
volumes:
postgres_data:
The manual entry even warns about your exact scenario (the parts in bold):
Control startup and shutdown order in Compose
You can control the order of service startup and shutdown with the
depends_on
attribute. Compose always starts and stops containers in dependency order, where dependencies are determined bydepends_on
,links
,volumes_from
, andnetwork_mode: "service:..."
.A good example of when you might use this is an application which needs to access a database. If both services are started with
docker compose up
, there is a chance this will fail since the application service might start before the database service and won't find a database able to handle its SQL statements.Control startup
On startup, Compose does not wait until a container is "ready", only until it's running. This can cause issues if, for example, you have a relational database system that needs to start its own services before being able to handle incoming connections.
The condition
and its corresponding healthcheck
command are straight from that doc. The pg_isready
comes bundled inside your postgres
image.
What you showed might've originally worked for the author of that tutorial and it might work for others if on their setup Postgres managed to start up fast enough. Or, in their case web
took longer before sending the requests, long enough for Postgres to finish starting up and catch them.
It might be tempting to try to add an initial sleep/wait/delay in your web
app, to let it give some time for the db
to initialise but it's safer to properly check and make sure. It's also clearer and easier to track if you define the dependency condition in the docker-compose.yml
rather than inside your app code, which shouldn't have to deal with matters related to environment setup.