Skip to content
Faizul Karim Fahim

Docker for Web Apps: A Practical Guide

Setting up Docker can feel overwhelming, but it’s really worth it for keeping my web apps consistent across environments. I’ll walk through the basics, from your first Dockerfile to running a simple PHP app.

Published 4 min read DevOps
A stylized illustration of shipping containers layered on top of a server rack, symbolizing Docker containers for web applications.
On this page
  1. What is Docker?
  2. Your First Dockerfile
  3. Building the Docker Image
  4. Volumes for Persistent Data
  5. My setup
  6. Troubleshooting Tips
  7. Further Learning
  8. Related reading

I've found Docker incredibly useful for managing my web applications, especially when dealing with different environments for clients. It eliminates a lot of the 'works on my machine' headaches. Here’s a breakdown of how I use Docker, from the very basics.

What is Docker?

Docker is a platform for packaging, distributing, and running applications using containers. These containers are lightweight, isolated environments that contain everything your app needs to run: code, runtime, system tools, system libraries, and settings. This ensures consistency across different environments.

Your First Dockerfile

Let's start with a simple PHP application. Create a Dockerfile in your project’s root directory:

FROM php:8.3-apache
WORKDIR /var/www/html
COPY . .
EXPOSE 80

This Dockerfile does the following:

  • FROM php:8.3-apache: Uses the official PHP 8.3 with Apache image as the base.
  • WORKDIR /var/www/html: Sets the working directory inside the container.
  • COPY . .: Copies all files from your project directory into the container.
  • EXPOSE 80: Exposes port 80, the standard HTTP port.

Building the Docker Image

Now, build the Docker image. From the directory containing your Dockerfile, run:

docker build -t my-php-app .```

`-t my-php-app` tags the image with a name, making it easier to identify later. The `.` specifies the current directory as the build context.

## Running the Container

To run the container, use the following command:

```bash
docker run -d -p 8080:80 my-php-app```

*   `-d`: Runs the container in detached mode (in the background).
*   `-p 8080:80`: Maps port 80 on the container to port 8080 on your host machine. Now you can access your app at `http://localhost:8080`.

## Docker Compose (for more complex apps)

For more complex applications, Docker Compose is your friend. It defines and manages multi-container Docker applications. Create a `docker-compose.yml` file:

```yaml
version: "3.9"
services:
  web:
    image: my-php-app
    ports: 
      - "8080:80"

To start the application, run:

docker-compose up -d```

This command builds (if necessary) and starts the services defined in your `docker-compose.yml` file.

## Networking Between Containers

Sometimes, your web app needs to talk to a database. Docker provides networking features to make this easy.  Let's say you have a MySQL database running in another container.  In your `docker-compose.yml`:

```yaml
version: "3.9"
services:
  web:
    image: my-php-app
    ports:
      - "8080:80"
    depends_on:
      - db
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: mysecretpassword
    ports:
      - "3306:3306"

The depends_on directive ensures the db service starts before the web service. The web container can then connect to the database using the service name db as the hostname.

Volumes for Persistent Data

By default, data inside containers is lost when the container is stopped or removed. To persist data, use Docker volumes. In your docker-compose.yml:

version: "3.9"
services:
  web:
    image: my-php-app
    ports:
      - "8080:80"
    volumes:
      - ./data:/var/www/html/data
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: mysecretpassword
    volumes:
      - db_data:/var/lib/mysql
volumes:
  db_data:

This mounts the ./data directory on your host machine to /var/www/html/data inside the web container, and creates a named volume db_data for the MySQL database.

My setup

On my servers, I usually use Ubuntu 24.04 for my development and staging environments and AlmaLinux 9 for production. I don't bother with fancy orchestration tools like Kubernetes for small VPSes; Docker Compose is usually enough. A common mistake I see is forgetting to expose ports correctly, leading to applications being inaccessible. Also, remember to systemctl reload nginx or Apache after making changes to your configuration files within the container, otherwise, they won't take effect.

Troubleshooting Tips

  • Image build fails: Double-check your Dockerfile for typos and ensure the build context contains all necessary files. I often get tripped up with case sensitivity.
  • Container doesn't start: Look at the container logs using docker logs <container_id> to identify errors.
  • Port conflicts: Ensure the ports you're mapping are not already in use on your host machine.
  • Permissions issues: When mounting volumes, make sure the user inside the container has the correct permissions to write to the mounted directory. I’ve spent too much time debugging this.

Further Learning

Docker has really simpler my workflow, and I hope this guide helps you get started too. It’s a fantastic tool for ensuring consistent deployments and simplifying application management. I find it’s a great complement to my Laravel development workflow, check out Automating Laravel Deployments with GitHub Actions for more ideas on how to automate deployments.

Frequently asked questions

Why should I use Docker?

Docker lets me package my app and its dependencies together, ensuring it runs the same way everywhere: my laptop, staging, production. It simplifies deployment and avoids the 'works on my machine' problem.

What's the difference between a Docker image and a container?

An image is like a template, a read-only snapshot. A container is a running instance of that image. Think of it like a blueprint versus a building.

Is Docker difficult to learn?

The basics are easy to pick up. The complexity comes with more advanced configurations like networking and volumes, but you can start with simple containers and build from there.

// keep reading

Related posts

All blog posts
Terminal screen showing successful deployment output on an Ubuntu server
Web Development 4 min read

Zero-Downtime Laravel Deployment on Ubuntu

I configure zero-downtime Laravel deployments on Ubuntu servers using Git, PHP 8.3, and basic shell scripts without complex CI/CD tools.

Illustration of a Laravel logo being deployed to an Ubuntu server via GitHub Actions workflow.
DevOps 2 min read

Automating Laravel Deployments with GitHub Actions

Setting up automated deployments for my Laravel projects used to be a headache, but GitHub Actions has made it significantly easier. Here's how I automate deployments to Ubuntu servers using GitHub Actions, and some common gotchas I've run into along the way.