Back to Blog
Lesson 22 of the Docker: Containers & Your First Image course
DevOpsAugust 9, 20264 min read

Connecting a Web App to a Database: Docker Service Integration

Master connecting your web app to a database service in Docker. Learn to use environment variables for secure connectivity and test your multi-tier app.

dockerdevopsdatabasecontainersnetworking
Vibrant close-up of code displayed on a monitor with various programming details.

Previously in this course, we covered defining service relationships in Docker Compose, where we established how services depend on one another. In this lesson, we take that a step further: we will move beyond simple orchestration and build a functional two-tier application by connecting a web service to a database.

Defining a Database Service

In a containerized environment, a database is just another service. You don't "install" it on your OS; you define it as a container image (like postgres or mysql) within your docker-compose.yml file.

When defining a database service, we must provide it with necessary configuration—such as initial credentials—via environment variables. Without these, the database container won't know which user to create or which password to enforce.

Here is a standard definition for a PostgreSQL database service:

YAML
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password123
      POSTGRES_DB: myapp_db
    ports:
      - "5432:5432"

By defining the db service here, Docker Compose handles the lifecycle of the database. When you run docker compose up, it pulls the image, sets the environment variables, and starts the process.

Providing Connectivity via Environment Variables

For your web application to find the database, it needs a "connection string." This string typically includes the hostname, port, database name, and credentials.

In Docker, the hostname is simply the name of the service defined in your Compose file. If your service is named db, your web app can reach it at the address db:5432.

You should inject these details into your web app container using environment variables rather than hardcoding them. This keeps your code portable and secure.

YAML
services:
  web:
    build: .
    environment:
      DATABASE_URL: postgresql://user:password123@db:5432/myapp_db
    depends_on:
      - db

Using this pattern, your application code can read the DATABASE_URL environment variable at runtime. Whether you are working in PHP, as seen in connecting to MySQL with PDO, or another language, the principle remains: decouple your configuration from your application logic.

Testing Connectivity

Once you have defined both services, you need to verify they can talk to each other. Because they are in the same Docker Compose project, they share an internal network automatically.

To test this, you can execute a command inside your running web container to see if it can resolve the database hostname:

Bash
# Verify the web container can see the 'db' host
docker compose exec web ping db

If the ping succeeds, your web app has the network path to reach the database. From there, you would use your application’s database client to initiate the connection. If you are building out your backend logic, consider how you might eventually fetch data from the database in Next.js Server Components to complete the integration.

Hands-on Exercise

  1. Create a docker-compose.yml file.
  2. Add a db service using the postgres:15 image.
  3. Add a web service (you can use a simple alpine image for testing).
  4. Set the POSTGRES_PASSWORD in the db service.
  5. Run docker compose up -d.
  6. Use docker compose exec web sh to enter the container and attempt to connect to the database host.

Common Pitfalls

  • Assuming localhost: Inside the web container, localhost refers to the container itself, not the host machine or the database container. Always use the service name (db) as the hostname.
  • Race Conditions: The database container might take a few seconds to initialize its internal storage before it is ready to accept connections. If your web app crashes on startup, it may be trying to connect before the database is "healthy."
  • Sensitive Data: While environment variables are standard for development, never commit clear-text passwords to your version control system. We will address better ways to handle secrets in a future lesson.

FAQ

Q: Can I access the database from my host machine? A: Yes, if you map the ports in your docker-compose.yml (e.g., "5432:5432"), you can connect to the database from your host using tools like DBeaver or psql.

Q: Do I need to install Postgres on my computer? A: No, that is the primary benefit of this approach. The database runs inside the container, keeping your host machine clean.

Recap

We have moved from running isolated containers to building a multi-tier stack. By defining a database service in Docker Compose and passing connection details through environment variables, we’ve created a robust, repeatable way to link our application code to its data storage. This foundation allows for real-world development where services interact seamlessly in an isolated environment.

Up next: We will dive into multi-container networking to understand how Docker manages the internal DNS and traffic between these services.

Similar Posts