Containers Intermediate

Troubleshooting ‘Port is Already Allocated’ (Bind Failed) in Docker Compose on CentOS Stream / Rocky Linux

Resolve the 'bind failed' error in Docker Compose when a port is already in use. Learn to identify and free up allocated ports on CentOS Stream and Rocky Linux systems.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve the 'bind failed' error in Docker Compose when a port is already in use. Learn to identify and free up allocated ports on CentOS Stream and Rocky Linux systems.

When deploying or restarting a Docker Compose application, encountering a port is already allocated or bind failed error is a common but frustrating issue. This error prevents your Docker container from starting, as it cannot claim the necessary network port. This guide will walk you through diagnosing and resolving this problem specifically on CentOS Stream and Rocky Linux distributions, ensuring your Docker Compose applications can run smoothly.

Symptom & Error Signature

You typically encounter this error when executing docker compose up -d (or docker-compose up -d for older versions) from your project directory. The output in your terminal will show a failed binding attempt, often preventing one or more services in your docker-compose.yml file from initializing.

Here's a common error signature you might see:

[+] Running 1/0
 ⠿ Container myapp-web-1  Error                                                                                                        0.0s
Error response from daemon: driver failed programming external connectivity on endpoint myapp-web-1 (...): Error starting userland proxy: listen tcp 0.0.0.0:80: bind: address already in use

Or a slightly different variation, particularly if Docker Compose is trying to bind to a specific IP:

ERROR: for web  Cannot start service web: driver failed programming external connectivity on endpoint myapp-web-1 (...): Error starting userland proxy: listen tcp 0.0.0.0:80: bind: address already in use

Or within the docker compose logs of a failed service:

myapp-web-1  | error: listen EADDRINUSE: address already in use 0.0.0.0:80

The key phrases are "bind: address already in use," "port is already allocated," or "EADDRINUSE."

Root Cause Analysis

The "port is already allocated" error fundamentally means that another process on your server is already listening on the specific TCP or UDP port that your Docker container is trying to use. When Docker attempts to map a host port to a container port (e.g., ports: - "80:80" in your docker-compose.yml), it requires exclusive access to that host port. If another application or service has already claimed it, the bind operation fails.

Common scenarios leading to this error include:

  1. Existing Web Server: Services like Nginx, Apache HTTP Server (httpd), or Caddy are often configured to listen on standard web ports like 80 (HTTP) or 443 (HTTPS). If your Docker application also tries to expose these ports, a conflict arises.
  2. Previous Docker Container: A previously running Docker container, perhaps from an earlier, failed docker compose up command or a different project, might still be occupying the port. This can happen if docker compose down was not executed correctly, or if a container was manually stopped but its port mapping persisted briefly.
  3. Other Application/Service: Any custom application, database, or proxy service running directly on the host system could be listening on the conflicted port.
  4. Stale Sockets/Zombie Processes: Less common, but sometimes a process might have terminated abnormally, leaving its network socket in a "TIME_WAIT" state or as a zombie process, preventing immediate reuse of the port.
  5. Multiple Docker Compose Projects: If you run multiple Docker Compose projects on the same host, and they are configured to expose the same host port, they will conflict.

Step-by-Step Resolution

Follow these steps to diagnose and resolve the port allocation issue on your CentOS Stream or Rocky Linux server.

1. Identify the Conflicting Process

The first step is to determine which process is currently listening on the port your Docker container wants to use. For this, we'll use the ss (socket statistics) utility, which is generally preferred over netstat on modern Linux systems due to its efficiency.

Let's assume your Docker Compose project is trying to bind to host port 80.

sudo ss -tulpn | grep ':80'
  • sudo: Required to see process names and PIDs for all listening sockets.
  • ss: Socket statistics utility.
  • -t: Show TCP sockets.
  • -u: Show UDP sockets.
  • -l: Show listening sockets.
  • -p: Show process using socket.
  • -n: Don't resolve service names (show port numbers).
  • grep ':80': Filter for lines containing :80 (or replace with your specific port, e.g., :443, :8080).

Example Output:

tcp    LISTEN     0      511                    0.0.0.0:80             0.0.0.0:*    users:(("nginx",pid=12345,fd=6))

From this output, we can see:

  • tcp: It's a TCP port.
  • LISTEN: The socket is in a listening state.
  • 0.0.0.0:80: The port 80 is bound to all available network interfaces.
  • users:(("nginx",pid=12345,fd=6)): The process nginx with PID 12345 is using this port.

2. Stop the Conflicting Process

Once you've identified the process and its PID, you need to stop it. The method depends on the type of process.

a. If it's a System Service (e.g., Nginx, Apache HTTPD)

If the conflicting process is a system service managed by systemd (like Nginx, Apache/httpd, or another application you've installed), stop it using systemctl.

# Example for Nginx
sudo systemctl stop nginx

# Example for Apache HTTP Server
sudo systemctl stop httpd

Stopping a production service like Nginx or Apache will make any websites it serves temporarily unavailable. Ensure you understand the impact before proceeding.

After stopping, verify the port is free:

sudo ss -tulpn | grep ':80'

The output should now be empty for that port.

b. If it's another Docker Container

If the ss output shows a Docker process or if you suspect another container, list all running containers:

docker ps

Look for containers that might be using the conflicting port. If you find one, stop it:

docker stop <container_id_or_name>

You can also inspect all containers, including stopped ones, to check their port mappings:

docker ps -a
docker inspect <container_id_or_name> | grep -i "portbindings" -A 5

If the container was part of another Docker Compose project, navigate to that project's directory and run docker compose down to gracefully stop and remove its services:

cd /path/to/other/docker-compose-project
docker compose down
c. If it's a Rogue or Custom Process

If the process is neither a standard system service nor a Docker container, it might be a custom script or an application that isn't managed by systemd. In such cases, you can terminate it directly using kill.

sudo kill <PID>

Replace <PID> with the Process ID you identified in Step 1 (e.g., 12345).

Avoid using kill -9 unless absolutely necessary (kill -9 sends an uncatchable SIGKILL signal, which can lead to data corruption if the process doesn't shut down cleanly). Start with sudo kill <PID> (which sends SIGTERM, allowing the process to clean up) and only use sudo kill -9 <PID> if the process persists after a few seconds.

After killing the process, verify the port is free:

sudo ss -tulpn | grep ':80'

3. Adjust Docker Compose Configuration (If Permanent Conflict)

If the conflict is intentional (e.g., you need Nginx to run on port 80, but your Docker app also needs port 80), or if you want to avoid future conflicts, modify your docker-compose.yml to use a different host port.

Change the ports mapping in your docker-compose.yml file. For example, to map host port 8080 to the container's port 80:

version: '3.8'
services:
  web:
    image: myapp/web:latest
    ports:
      - "8080:80" # Change host port from 80 to 8080
    # ... other configurations

This change will require you to access your application via http://your_server_ip:8080 instead of the default http://your_server_ip. If you're using Nginx as a reverse proxy, you'd configure Nginx to proxy requests from port 80 to localhost:8080.

4. Restart Your Docker Compose Application

Once the conflicting process is stopped and the port is free (or your docker-compose.yml is updated), you can try starting your application again.

Navigate to your Docker Compose project directory and run:

docker compose up -d

The -d flag runs the containers in detached mode.

Verify that your containers are running and listening on the correct ports:

docker ps

You should see your service listed with its port mappings.

5. Configure Firewall (Optional but Recommended)

On CentOS Stream and Rocky Linux, firewalld is the default firewall management tool. If your Docker container is listening on a new port (e.g., 8080) and you want it accessible from outside your server, you'll need to open that port in firewalld.

# Add TCP port 8080 to the public zone permanently
sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent

# Reload firewalld to apply changes
sudo firewall-cmd --reload

Docker typically manages its own iptables rules, bypassing firewalld for internal container-to-host port mapping. However, explicitly opening ports with firewall-cmd ensures external access for non-standard ports.

By systematically identifying and addressing the process occupying the desired port, you can effectively resolve the port is already allocated error and get your Docker Compose applications running as intended.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.