Skip to main content

Command Palette

Search for a command to run...

Day 40: Docker Container Configuration - Installing and Configuring Services 🔧

Published
23 min read
Day 40: Docker Container Configuration - Installing and Configuring Services 🔧

Welcome back! 👋 Day 40 of the 100 Days Cloud DevOps Challenge, and today we're mastering container service configuration! This is real-world DevOps - installing software, configuring services, and managing applications inside running containers. Let's configure! 🎯

🎯 The Mission - Complete Apache Configuration in Container

It's urgent task completion day - finishing a teammate's work:

📋 TASK TICKET #DEV-8040 - Apache Configuration in Container
Priority: URGENT
Type: Container Service Configuration
Status: Incomplete - Original assignee on PTO

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PROJECT: KKloud Container Services
Team: Nautilus DevOps
Location: Stratos Datacenter
Server: App Server 1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

BACKGROUND:
└─ Team member started configuration
└─ Container: kkloud (running on App Server 1)
└─ Work incomplete due to PTO
└─ Need immediate completion

REQUIREMENTS:

1. Install Apache2:
   └─ Container: kkloud (running)
   └─ Package: apache2
   └─ Method: apt package manager
   └─ Server: App Server 1 (stapp01)

2. Configure Custom Port:
   └─ Change from default port 80
   └─ New port: 5002
   └─ Listen on ALL interfaces
   └─ NOT specific IP/hostname
   └─ Accept: localhost, 127.0.0.1, container IP

3. Start Apache Service:
   └─ Service must be running
   └─ Verify accessibility on port 5002
   └─ Container stays running
   └─ Apache persists in container

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
STATUS: URGENT - Complete ASAP
DEADLINE: Today
CRITICAL: Keep container running!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This is production container management! Real-world service configuration and troubleshooting! 🚀

🤔 Why Container Service Configuration Matters

The Container Application Problem

Scenario: Need web server in container

Without proper configuration:

Developer:
├─ docker run ubuntu
├─ Try to access web server
└─ Nothing works! ❌

Problems:
❌ No web server installed
❌ Wrong port configured
❌ Service not running
❌ Container not persistent

With proper configuration:

DevOps workflow:

1. Install service (apache2)
2. Configure port (5002)
3. Start service
4. Verify running
5. Keep container alive

Result:
✅ Apache installed
✅ Custom port configured
✅ Service running
✅ Accessible on port 5002
✅ Container persists

Real stat: 71% of containerized applications require custom service configuration! 📊

Understanding Apache in Containers

Apache HTTP Server:

  • World's most popular web server

  • Powers 31% of all websites

  • Highly configurable

  • Production-grade reliability

Why Apache in containers:

Traditional deployment:
├─ Install on host OS
├─ Conflicts with other services
├─ Difficult to isolate
└─ Hard to replicate

Container deployment:
├─ Isolated environment
├─ No host conflicts
├─ Easy replication
├─ Portable configuration
└─ Version control

Port Configuration Importance

Default Apache port: 80

Problems with default:
├─ Requires root privileges
├─ Conflicts with other services
├─ Security concerns
└─ Not flexible for multi-service

Custom port: 5002

Benefits:
├─ Non-privileged port (> 1024)
├─ Avoids conflicts
├─ Multiple services possible
└─ Better security isolation

Listen on all interfaces:

Specific IP (0.0.0.0:5002):
└─ Binds to all interfaces
└─ Accepts connections from anywhere

With hostname (localhost:5002):
└─ Only local connections
└─ External access blocked

Our requirement: Listen on ALL
└─ 0.0.0.0:5002 or *:5002
└─ Accepts from container IP, localhost, etc.

🏗️ Understanding the Setup

App Server 1 Details:

Server: stapp01
User: tony
Password: Ir0nM@n
Role: Application Server 1

Current State:
├─ Docker running
├─ Container: kkloud (running)
│  ├─ Base: Ubuntu/Debian based
│  ├─ Apache2: NOT installed
│  └─ Status: Running but empty
└─ Target: Configure Apache on port 5002

After Configuration:
├─ Container: kkloud (running)
├─ Apache2: Installed ✓
├─ Port: 5002 (configured) ✓
├─ Service: Running ✓
└─ Accessible: Yes ✓

Apache Configuration Architecture:

kkloud Container:

┌─────────────────────────────────────┐
│  Apache2 Service                    │
│  ├─ Binary: /usr/sbin/apache2      │
│  ├─ Config: /etc/apache2/          │
│  │  ├─ ports.conf (Listen 5002)   │
│  │  └─ sites-available/            │
│  ├─ Process: Running (PID 1234)    │
│  └─ Listening: 0.0.0.0:5002       │
├─────────────────────────────────────┤
│  Container Filesystem               │
│  ├─ /var/www/html/ (web root)     │
│  └─ /var/log/apache2/ (logs)      │
└─────────────────────────────────────┘

Port Binding:
└─ Container port 5002 → Apache
   └─ Accessible from localhost, container IP

Configuration Flow:

Step 1: Install Apache2
apt-get update → apt-get install apache2

Step 2: Configure Port
Edit /etc/apache2/ports.conf
Listen 80 → Listen 5002

Edit /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80> → <VirtualHost *:5002>

Step 3: Start Service
service apache2 start

Step 4: Verify
curl http://localhost:5002
netstat -tulpn | grep 5002

Step 5: Keep Running
Container must stay active

🛠️ Complete Step-by-Step Implementation

Phase 1: Access and Verify Environment

Step 1.1: SSH to App Server 1

bash

# Connect to App Server 1
ssh tony@stapp01
# Password: Ir0nM@n

You're logged in!

Step 1.2: Verify Docker Service

bash

# Check Docker is running
sudo systemctl status docker

Expected output:

● docker.service - Docker Application Container Engine
   Active: active (running)

Docker operational!

Step 1.3: Find kkloud Container

bash

# List running containers
sudo docker ps

Expected output:

CONTAINER ID   IMAGE     COMMAND       CREATED        STATUS        NAMES
a1b2c3d4e5f6   ubuntu    "/bin/bash"   2 hours ago    Up 2 hours    kkloud

kkloud container running!

If not running:

bash

# Start the container
sudo docker start kkloud

Step 1.4: Check Container Base Image

bash

# Identify OS in container
sudo docker exec kkloud cat /etc/os-release

Expected output (Ubuntu/Debian):

NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"
...

Container is Ubuntu-based (apt available)!

Step 1.5: Verify Internet Connectivity

bash

# Test network from container
sudo docker exec kkloud ping -c 2 google.com

Ping succeeds = can download packages!

Phase 2: Install Apache2

Step 2.1: Update Package Lists

bash

# Update apt cache in container
sudo docker exec kkloud apt-get update

Expected output:

Get:1 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
Get:2 http://archive.ubuntu.com/ubuntu focal-updates InRelease [114 kB]
...
Fetched 25.3 MB in 8s (3,072 kB/s)
Reading package lists... Done

Package lists updated!

Step 2.2: Install Apache2

bash

# Install apache2 package
sudo docker exec kkloud apt-get install -y apache2

Expected output:

Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  apache2-bin apache2-data apache2-utils libapr1 libaprutil1 ...
Suggested packages:
  apache2-doc apache2-suexec-pristine | apache2-suexec-custom
The following NEW packages will be installed:
  apache2 apache2-bin apache2-data apache2-utils ...
0 upgraded, 20 newly installed, 0 to remove and 0 not upgraded.
Need to get 6,234 kB of archives.
After this operation, 25.6 MB of additional disk space will be used.
...
Setting up apache2 (2.4.41-4ubuntu3) ...

Apache2 installed! 🎉

Step 2.3: Verify Apache Installation

bash

# Check apache2 binary exists
sudo docker exec kkloud which apache2

Expected output:

/usr/sbin/apache2

Apache2 binary present!

Step 2.4: Check Apache Version

bash

# Verify Apache version
sudo docker exec kkloud apache2 -v

Expected output:

Server version: Apache/2.4.41 (Ubuntu)
Server built:   2023-10-04T14:42:48

Apache installed correctly!

Step 2.5: Check Default Configuration

bash

# List Apache config files
sudo docker exec kkloud ls -la /etc/apache2/

Expected output:

drwxr-xr-x 8 root root  4096 Dec 12 10:00 .
drwxr-xr-x 1 root root  4096 Dec 12 10:00 ..
-rw-r--r-- 1 root root  7224 Oct  4 14:42 apache2.conf
drwxr-xr-x 2 root root  4096 Dec 12 10:00 conf-available
drwxr-xr-x 2 root root  4096 Dec 12 10:00 conf-enabled
drwxr-xr-x 2 root root  4096 Dec 12 10:00 mods-available
drwxr-xr-x 2 root root  4096 Dec 12 10:00 mods-enabled
-rw-r--r-- 1 root root   320 Oct  4 14:42 ports.conf
drwxr-xr-x 2 root root  4096 Dec 12 10:00 sites-available
drwxr-xr-x 2 root root  4096 Dec 12 10:00 sites-enabled

Configuration directory present!

Phase 3: Configure Apache Port

Step 3.1: Check Current Port Configuration

bash

# View current ports.conf
sudo docker exec kkloud cat /etc/apache2/ports.conf

Expected output:

Listen 80

<IfModule ssl_module>
        Listen 443
</IfModule>

<IfModule mod_gnutls.c>
        Listen 443
</IfModule>

Default is port 80 - need to change to 5002! 📝

Step 3.2: Backup Original Configuration

bash

# Create backup of ports.conf
sudo docker exec kkloud cp /etc/apache2/ports.conf /etc/apache2/ports.conf.bak

Backup created!

Step 3.3: Modify ports.conf

bash

# Change Listen port from 80 to 5002
sudo docker exec kkloud sed -i 's/Listen 80/Listen 5002/g' /etc/apache2/ports.conf

Port configuration updated!

Step 3.4: Verify ports.conf Change

bash

# Confirm port changed
sudo docker exec kkloud cat /etc/apache2/ports.conf

Expected output:

Listen 5002

<IfModule ssl_module>
        Listen 443
</IfModule>
...

Listen 5002 configured!

Step 3.5: Check Default VirtualHost

bash

# View default site configuration
sudo docker exec kkloud cat /etc/apache2/sites-available/000-default.conf | grep VirtualHost

Expected output:

<VirtualHost *:80>

VirtualHost still on port 80 - need to change! 📝

Step 3.6: Update VirtualHost Port

bash

# Change VirtualHost port from 80 to 5002
sudo docker exec kkloud sed -i 's/<VirtualHost \*:80>/<VirtualHost *:5002>/g' /etc/apache2/sites-available/000-default.conf

VirtualHost updated!

Step 3.7: Verify VirtualHost Change

bash

# Confirm VirtualHost changed
sudo docker exec kkloud cat /etc/apache2/sites-available/000-default.conf | grep VirtualHost

Expected output:

<VirtualHost *:5002>

VirtualHost on port 5002!

Note: *:5002 means listen on ALL interfaces (any IP) on port 5002 ✓

Step 3.8: Check for Other Port References

bash

# Search for any remaining port 80 references
sudo docker exec kkloud grep -r "80" /etc/apache2/sites-enabled/ 2>/dev/null || echo "No port 80 found in enabled sites"

Verify no port 80 in enabled sites!

Phase 4: Start Apache Service

Step 4.1: Start Apache

bash

# Start Apache service
sudo docker exec kkloud service apache2 start

Expected output:

 * Starting Apache httpd web server apache2
 *

Apache started! 🎊

Alternative methods:

bash

# Using systemctl (if available)
sudo docker exec kkloud systemctl start apache2

# Using apache2ctl
sudo docker exec kkloud apache2ctl start

Step 4.2: Check Apache Status

bash

# Verify Apache is running
sudo docker exec kkloud service apache2 status

Expected output:

 * apache2 is running

Service active!

Step 4.3: Check Apache Process

bash

# List Apache processes
sudo docker exec kkloud ps aux | grep apache2

Expected output:

root      1234  0.0  1.2  12345  12345 ?   Ss   10:00   0:00 /usr/sbin/apache2 -k start
www-data  1235  0.0  0.8  12345  12345 ?   S    10:00   0:00 /usr/sbin/apache2 -k start
www-data  1236  0.0  0.8  12345  12345 ?   S    10:00   0:00 /usr/sbin/apache2 -k start

Apache processes running!

Step 4.4: Verify Port Binding

bash

# Check if Apache is listening on port 5002
sudo docker exec kkloud netstat -tulpn | grep 5002

Expected output:

tcp        0      0 0.0.0.0:5002            0.0.0.0:*               LISTEN      1234/apache2
tcp6       0      0 :::5002                 :::*                    LISTEN      1234/apache2

Apache listening on 0.0.0.0:5002 (all interfaces)!

If netstat not available:

bash

# Install net-tools if needed
sudo docker exec kkloud apt-get install -y net-tools

# Or use ss command
sudo docker exec kkloud ss -tulpn | grep 5002

Phase 5: Verify Apache Accessibility

Step 5.1: Test from Localhost

bash

# Access Apache from within container (localhost)
sudo docker exec kkloud curl -I http://localhost:5002

Expected output:

HTTP/1.1 200 OK
Date: Thu, 12 Dec 2025 10:00:00 GMT
Server: Apache/2.4.41 (Ubuntu)
Last-Modified: Thu, 12 Dec 2025 09:55:00 GMT
Content-Type: text/html

HTTP 200 - Apache serving on localhost:5002!

Step 5.2: Test from 127.0.0.1

bash

# Access via 127.0.0.1
sudo docker exec kkloud curl -I http://127.0.0.1:5002

Expected output:

HTTP/1.1 200 OK
...

Accessible on 127.0.0.1:5002!

Step 5.3: Get Container IP

bash

# Find container IP address
CONTAINER_IP=$(sudo docker inspect kkloud --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
echo "Container IP: $CONTAINER_IP"

Expected output:

Container IP: 172.17.0.2

Container IP identified!

Step 5.4: Test from Container IP

bash

# Access via container IP
sudo docker exec kkloud curl -I http://$CONTAINER_IP:5002

Expected output:

HTTP/1.1 200 OK
...

Accessible on container IP:5002!

Step 5.5: Get Full Page Content

bash

# Retrieve actual page content
sudo docker exec kkloud curl http://localhost:5002

Expected output:

html

<!DOCTYPE html>
<html>
<head>
<title>Apache2 Ubuntu Default Page</title>
...
</head>
<body>
<div class="page_header floating_element">
<img src="/icons/ubuntu-logo.png" alt="Ubuntu Logo" class="floating_element"/>
<span class="floating_element">
Apache2 Ubuntu Default Page
</span>
...

Apache default page served!

Phase 6: Ensure Container Stays Running

Step 6.1: Check Container Status

bash

# Verify container is still running
sudo docker ps --filter "name=kkloud"

Expected output:

CONTAINER ID   IMAGE     COMMAND       CREATED      STATUS      NAMES
a1b2c3d4e5f6   ubuntu    "/bin/bash"   3 hours ago  Up 3 hours  kkloud

Container still running!

Step 6.2: Keep Apache Running

bash

# Ensure Apache stays up (if not already configured)
# Apache should continue running as long as container is active
sudo docker exec kkloud service apache2 status

Apache persists in running container!

Step 6.3: Test Container Persistence

bash

# Container should not exit
# Original command (likely /bin/bash or similar) keeps it alive
sudo docker inspect kkloud --format='{{.State.Status}}'

Expected output:

running

Container in running state!

Phase 7: Final Comprehensive Verification

Step 7.1: Complete Service Check

bash

# Comprehensive verification script
echo "=== Apache Configuration Verification ==="
echo ""
echo "1. Container Status:"
sudo docker ps --filter "name=kkloud" --format "   {{.Names}}: {{.Status}}"
echo ""
echo "2. Apache Installation:"
sudo docker exec kkloud which apache2
echo ""
echo "3. Apache Service:"
sudo docker exec kkloud service apache2 status | head -1
echo ""
echo "4. Port Configuration:"
sudo docker exec kkloud grep "Listen" /etc/apache2/ports.conf | head -1
echo ""
echo "5. VirtualHost Configuration:"
sudo docker exec kkloud grep "VirtualHost" /etc/apache2/sites-available/000-default.conf | head -1
echo ""
echo "6. Listening Port:"
sudo docker exec kkloud netstat -tulpn | grep 5002 | head -1
echo ""
echo "7. HTTP Response:"
sudo docker exec kkloud curl -s -o /dev/null -w "   HTTP Status: %{http_code}\n" http://localhost:5002
echo ""
echo "Status: ✓ ALL CHECKS PASSED"

Expected complete output:

=== Apache Configuration Verification ===

1. Container Status:
   kkloud: Up 3 hours

2. Apache Installation:
/usr/sbin/apache2

3. Apache Service:
 * apache2 is running

4. Port Configuration:
Listen 5002

5. VirtualHost Configuration:
<VirtualHost *:5002>

6. Listening Port:
tcp        0      0 0.0.0.0:5002            0.0.0.0:*               LISTEN      1234/apache2

7. HTTP Response:
   HTTP Status: 200

Status: ✓ ALL CHECKS PASSED

COMPLETE SUCCESS! 🎉🎊🎈🎇

Step 7.2: Test from Host (Optional)

bash

# Get container IP
CONTAINER_IP=$(sudo docker inspect kkloud --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')

# Test from host
curl -I http://$CONTAINER_IP:5002

Accessible from Docker host!

Step 7.3: View Apache Logs

bash

# Check Apache access log
sudo docker exec kkloud tail -5 /var/log/apache2/access.log

Shows recent access requests!

Step 7.4: Final State Documentation

bash

# Document final state
echo "=== Final Configuration Summary ===" | tee apache-config-summary.txt
echo "" | tee -a apache-config-summary.txt
echo "Container: kkloud" | tee -a apache-config-summary.txt
echo "Service: Apache2 $(sudo docker exec kkloud apache2 -v | head -1)" | tee -a apache-config-summary.txt
echo "Port: 5002" | tee -a apache-config-summary.txt
echo "Listen: 0.0.0.0:5002 (all interfaces)" | tee -a apache-config-summary.txt
echo "Status: Running ✓" | tee -a apache-config-summary.txt
echo "Tested: localhost, 127.0.0.1, container IP ✓" | tee -a apache-config-summary.txt
echo "" | tee -a apache-config-summary.txt
echo "Configuration complete at: $(date)" | tee -a apache-config-summary.txt

Documentation created!

TASK COMPLETE - APACHE CONFIGURED AND RUNNING! 🚀

🔍 Understanding What We Accomplished

Apache Installation Process

What apt-get install apache2 did:

Packages Installed:
├─ apache2 (main package)
├─ apache2-bin (binaries)
├─ apache2-data (default files)
├─ apache2-utils (utilities)
├─ libapr1 (Apache Portable Runtime)
├─ libaprutil1 (APR utilities)
└─ Dependencies (20+ packages)

Files Created:
├─ /usr/sbin/apache2 (main binary)
├─ /etc/apache2/ (configuration)
│  ├─ apache2.conf (main config)
│  ├─ ports.conf (port settings)
│  ├─ sites-available/ (virtual hosts)
│  └─ mods-available/ (modules)
├─ /var/www/html/ (web root)
│  └─ index.html (default page)
└─ /var/log/apache2/ (logs)
   ├─ access.log
   └─ error.log

Port Configuration Explained

Two files modified:

1. /etc/apache2/ports.conf:

apache

# Before:
Listen 80

# After:
Listen 5002

Purpose: Global listening port
Effect: Apache binds to port 5002 on ALL interfaces

2. /etc/apache2/sites-available/000-default.conf:

apache

# Before:
<VirtualHost *:80>

# After:
<VirtualHost *:5002>

Purpose: Virtual host configuration
Effect: Default site serves on port 5002

Why both needed:

ports.conf:
└─ Tells Apache which ports to listen on
└─ Global setting for entire server

VirtualHost:
└─ Tells Apache which port to serve content on
└─ Site-specific setting

Both must match or Apache won't serve correctly!

Listen Directive Analysis

Our configuration: Listen 5002

What it means:
├─ No IP specified = 0.0.0.0 (all interfaces)
├─ Port 5002 specified
└─ Accepts connections from:
   ├─ localhost (127.0.0.1)
   ├─ Container IP (172.17.0.x)
   ├─ Docker host
   └─ Any routed network

Alternative (NOT what we want):
Listen 127.0.0.1:5002
└─ Only localhost (blocks container IP access)

Listen 172.17.0.2:5002
└─ Only container IP (blocks localhost)

Our Listen 5002 = Listen 0.0.0.0:5002
└─ ALL interfaces ✓ (meets requirements!)

Service Management

How Apache runs in container:

Process Tree:
Container PID 1 (/bin/bash or init)
└─ Apache Master Process (root)
   ├─ Apache Worker 1 (www-data)
   ├─ Apache Worker 2 (www-data)
   └─ Apache Worker N (www-data)

Why container stays running:
├─ Original container command keeps it alive
├─ Apache runs as background service
├─ Container doesn't exit as long as PID 1 exists
└─ Apache continues serving requests

💡 Key Takeaways

apt-get install works in Ubuntu/Debian containers

ports.conf sets global listening ports

VirtualHost must match ports.conf

Listen 5002 = listen on all interfaces (0.0.0.0)

service apache2 start launches the service

netstat -tulpn shows listening ports

curl localhost:5002 tests accessibility

Container must stay running (don't exit)

Apache persists as background service

Always verify with multiple tests

🎓 Interview Questions

Q1: Why do we need to modify both ports.conf and the VirtualHost configuration?

Answer: They serve different purposes in Apache's architecture. ports.conf: Global binding directive, tells Apache which ports to open sockets on, controls Listen statements. Example: Listen 5002 means Apache binds to 0.0.0.0:5002. VirtualHost: Site-specific configuration, tells Apache which port serves which content, controls request routing. Example: <VirtualHost *:5002> means this site serves on port 5002. Why both needed: If ports.conf says Listen 5002 but VirtualHost says *:80, Apache opens port 5002 but serves content on mismatched port (doesn't work). If VirtualHost says *:5002 but ports.conf says Listen 80, Apache doesn't bind to 5002 (doesn't work). Both must match: ports.conf opens the port, VirtualHost serves content on it. Real example: Changed only ports.conf → Apache binds to 5002 but VirtualHost expects 80 → 404 errors. Best practice: Always update both files, verify with apache2ctl configtest, restart service after changes.

Q2: What's the difference between Listen 5002, Listen 0.0.0.0:5002, and Listen 127.0.0.1:5002?

Answer: Different interface binding behaviors. Listen 5002: No IP specified, defaults to 0.0.0.0 (all interfaces), accepts connections from anywhere, accessible via localhost, container IP, host IP. Listen 0.0.0.0:5002: Explicit all interfaces, identical to Listen 5002, binds to every network interface, most permissive configuration. Listen 127.0.0.1:5002: Loopback only, only accepts from localhost/127.0.0.1, blocks container IP access, blocks external connections. Our requirement: "Listen on ALL interfaces", must accept localhost AND container IP, solution: Listen 5002 or Listen 0.0.0.0:5002. Testing: Listen 5002 → curl http://localhost:5002 ✓, curl http://172.17.0.2:5002 ✓. Listen 127.0.0.1:5002 → curl http://localhost:5002 ✓, curl http://172.17.0.2:5002 ✗ (connection refused). Use cases: 0.0.0.0 for public services, 127.0.0.1 for internal/admin interfaces, specific IP for multi-homed servers.

Q3: How do you troubleshoot if Apache starts but doesn't respond on the configured port?

Answer: Systematic debugging approach. Step 1: Verify service running: service apache2 status or ps aux | grep apache2. If not running, check error logs: tail /var/log/apache2/error.log. Step 2: Check port binding: netstat -tulpn | grep 5002 or ss -tulpn | grep 5002. If port not listed, Apache didn't bind (config error). Step 3: Test locally first: curl http://localhost:5002 from inside container. If fails, Apache not serving (config problem). Step 4: Check configuration: apache2ctl configtest shows syntax errors. Verify ports.conf and VirtualHost match. Step 5: Check firewall: iptables -L (usually not issue in containers). Step 6: Verify permissions: Check /var/www/html/ ownership, ensure www-data can read files. Common issues: ports.conf says 5002, VirtualHost says 80 (mismatch), SELinux blocking (rare in containers), another process using port 5002. Solution pattern: Check logs first (80% of issues visible), verify config files match, test incrementally (localhost → container IP → host).

Q4: How would you make Apache start automatically when the container starts?

Answer: Several approaches depending on container setup. Method 1: Modify container's CMD/ENTRYPOINT (best for production): bash # Commit container with Apache autostart docker commit --change='CMD service apache2 start && tail -f /var/log/apache2/access.log' kkloud kkloud:apache # Or use init system docker commit --change='CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]' kkloud kkloud:apache Method 2: Create startup script: bash # In container echo '#!/bin/bash' > /start.sh echo 'service apache2 start' >> /start.sh echo 'tail -f /dev/null' >> /start.sh chmod +x /start.sh # Set as entrypoint docker commit --change='CMD ["/start.sh"]' kkloud kkloud:apache Method 3: Use supervisor: Install supervisor, configure to manage Apache, keeps processes running. Method 4: Docker Compose (multi-container): yaml services: web: image: kkloud:apache command: apache2ctl -D FOREGROUND Our scenario: Container already running with existing CMD, Apache started manually, persists as background service. Best practice: For production, use Dockerfile with proper CMD/ENTRYPOINT, test container restarts.

Q5: What's the difference between 'service apache2 start', 'systemctl start apache2', and 'apache2ctl start'?

Answer: Three different service management approaches. service apache2 start (SysVinit): Traditional init script, works on older systems and containers, calls /etc/init.d/apache2 start, compatible with most environments. systemctl start apache2 (systemd): Modern systemd command, manages services via systemd, requires systemd running (not always in containers), more features (dependency management, socket activation). apache2ctl start (Apache control): Apache-specific utility, directly controls Apache without init system, useful when init system unavailable, fewer features than service managers. Container context: Many containers lack systemd (too heavy), service command often works (lightweight), apache2ctl most reliable in containers. Our task: Used service apache2 start (common in containers), could also use apache2ctl start, systemctl might not work (no systemd). For foreground: apache2ctl -D FOREGROUND (keeps container alive), service apache2 start (background daemon). Best practice: In containers, prefer apache2ctl or direct binary, in VMs, use systemctl or service.

Q6: How do you verify that Apache is listening on ALL interfaces and not just localhost?

Answer: Multiple verification methods. Method 1: netstat/ss output analysis: bash netstat -tulpn | grep 5002 # Output: tcp 0 0 0.0.0.0:5002 ... LISTEN # 0.0.0.0 = all IPv4 interfaces ✓ # tcp6 0 0 :::5002 ... LISTEN # ::: = all IPv6 interfaces ✓ # If shows 127.0.0.1:5002 = localhost only ✗ Method 2: Test multiple interfaces: bash # Test localhost curl http://localhost:5002 # Test 127.0.0.1 curl http://127.0.0.1:5002 # Test container IP curl http://172.17.0.2:5002 # All should respond ✓ Method 3: Check Apache configuration: bash grep "Listen" /etc/apache2/ports.conf # Listen 5002 (no IP) = all interfaces ✓ # Listen 127.0.0.1:5002 = localhost only ✗ Method 4: From Docker host: bash # Get container IP CONTAINER_IP=$(docker inspect kkloud --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}') # Test from host curl http://$CONTAINER_IP:5002 # If works = accessible from outside ✓ Our requirement met: netstat shows 0.0.0.0:5002 ✓, curl localhost works ✓, curl container IP works ✓, configuration is Listen 5002 (no IP restriction) ✓.

🌟 Apache Configuration Commands Reference

Installation:

bash

# Update package lists
apt-get update

# Install Apache
apt-get install -y apache2

# Check installation
which apache2
apache2 -v

Configuration:

bash

# Edit port configuration
vi /etc/apache2/ports.conf
# Change: Listen 80 → Listen 5002

# Edit VirtualHost
vi /etc/apache2/sites-available/000-default.conf
# Change: <VirtualHost *:80> → <VirtualHost *:5002>

# Test configuration
apache2ctl configtest

# Or using sed (automated)
sed -i 's/Listen 80/Listen 5002/g' /etc/apache2/ports.conf
sed -i 's/<VirtualHost \*:80>/<VirtualHost *:5002>/g' /etc/apache2/sites-available/000-default.conf

Service Management:

bash

# Start Apache
service apache2 start
# or
apache2ctl start

# Stop Apache
service apache2 stop

# Restart Apache
service apache2 restart
# or
apache2ctl restart

# Reload configuration (no downtime)
service apache2 reload
# or
apache2ctl graceful

# Check status
service apache2 status

Verification:

bash

# Check processes
ps aux | grep apache2

# Check listening ports
netstat -tulpn | grep apache
# or
ss -tulpn | grep apache

# Test HTTP response
curl http://localhost:5002
curl -I http://localhost:5002  # Headers only

# View logs
tail -f /var/log/apache2/access.log
tail -f /var/log/apache2/error.log

🚀 Real-World Scenarios

Scenario 1: Multiple Services in Container

bash

# Install multiple web services
apt-get install -y apache2 nginx

# Configure different ports
# Apache on 5002
sed -i 's/Listen 80/Listen 5002/g' /etc/apache2/ports.conf

# Nginx on 8080
sed -i 's/listen 80/listen 8080/g' /etc/nginx/sites-available/default

# Start both
service apache2 start
service nginx start

# Both services coexist
curl http://localhost:5002  # Apache
curl http://localhost:8080  # Nginx

Scenario 2: SSL Configuration

bash

# Enable SSL module
a2enmod ssl

# Create self-signed certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /etc/ssl/private/apache.key \
  -out /etc/ssl/certs/apache.crt

# Configure SSL VirtualHost
vi /etc/apache2/sites-available/default-ssl.conf
# Change: <VirtualHost *:443> → <VirtualHost *:5003>

# Add to ports.conf
echo "Listen 5003" >> /etc/apache2/ports.conf

# Enable site
a2ensite default-ssl

# Restart
service apache2 restart

Scenario 3: Custom Web Application

bash

# Install PHP
apt-get install -y php libapache2-mod-php

# Create application
cat > /var/www/html/info.php << 'EOF'
<?php
phpinfo();
?>
EOF

# Test
curl http://localhost:5002/info.php

Scenario 4: Reverse Proxy Setup

bash

# Enable proxy modules
a2enmod proxy
a2enmod proxy_http

# Configure reverse proxy
cat >> /etc/apache2/sites-available/000-default.conf << 'EOF'
ProxyPass /app http://backend:8080/
ProxyPassReverse /app http://backend:8080/
EOF

# Restart
service apache2 restart

Scenario 5: Performance Tuning

bash

# Enable MPM event (better performance)
a2dismod mpm_prefork
a2enmod mpm_event

# Configure worker settings
vi /etc/apache2/mods-available/mpm_event.conf
# Adjust StartServers, MinSpareThreads, etc.

# Enable compression
a2enmod deflate

# Restart
service apache2 restart

🎯 Best Practices

1. Always verify configuration before restart:

bash

# Test configuration syntax
apache2ctl configtest
# Should return: Syntax OK

# If errors, fix before restarting

2. Use non-privileged ports:

bash

# Good: Port > 1024
Listen 5002  # No root needed for process

# Bad: Port < 1024
Listen 80    # Requires root privileges

3. Log everything for debugging:

bash

# Monitor logs in real-time
tail -f /var/log/apache2/access.log &
tail -f /var/log/apache2/error.log &

# Make changes and watch for errors

4. Backup configurations:

bash

# Before modifying
cp /etc/apache2/ports.conf /etc/apache2/ports.conf.bak
cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf.bak

# Can revert if issues

5. Use version control for configs:

bash

# Initialize git in config directory
cd /etc/apache2
git init
git add .
git commit -m "Initial Apache configuration"

# After changes
git diff  # See what changed
git commit -am "Changed port to 5002"

6. Document custom configurations:

bash

# Add comments to config files
cat >> /etc/apache2/ports.conf << 'EOF'
# Changed to port 5002 for kkloud container
# Date: 2025-12-12
# Ticket: DEV-8040
Listen 5002
EOF

⚠️ Common Issues and Solutions

Issue 1: Apache fails to start

bash

# Check error logs
tail -50 /var/log/apache2/error.log

# Common causes:
# - Port already in use
# - Configuration syntax error
# - Missing modules

# Solutions:
# Kill process using port
fuser -k 5002/tcp

# Test config
apache2ctl configtest

# Enable required modules
a2enmod rewrite

Issue 2: Permission denied errors

bash

# Fix ownership
chown -R www-data:www-data /var/www/html/

# Fix permissions
chmod -R 755 /var/www/html/

Issue 3: Changes not taking effect

bash

# Reload might not be enough
service apache2 restart

# Or full stop/start
service apache2 stop
sleep 2
service apache2 start

Issue 4: Container exits after Apache starts

bash

# Apache runs in background, container needs foreground process

# Solution 1: Run Apache in foreground
apache2ctl -D FOREGROUND

# Solution 2: Add tail command
service apache2 start && tail -f /dev/null

# Solution 3: Use proper init
CMD service apache2 start && /bin/bash

Issue 5: Can't access from outside container

bash

# Check port mapping (if using -p flag)
docker ps  # Look for port mappings

# Should have: 0.0.0.0:5002->5002/tcp

# If not mapped, can't access from host
# Solution: Restart with -p flag
docker run -p 5002:5002 ...

📊 Performance Monitoring

Check Apache performance:

bash

# Enable server-status module
a2enmod status

# Add to config
cat >> /etc/apache2/sites-available/000-default.conf << 'EOF'
<Location "/server-status">
    SetHandler server-status
    Require local
</Location>
EOF

# Restart Apache
service apache2 restart

# View status
curl http://localhost:5002/server-status

# Monitor in real-time
watch -n 1 'curl -s http://localhost:5002/server-status?auto'

Resource usage:

bash

# Check Apache memory usage
ps aux | grep apache2 | awk '{sum+=$6} END {print sum/1024 " MB"}'

# Check connections
netstat -an | grep :5002 | wc -l

# Check request rate
tail -f /var/log/apache2/access.log | pv -l -i 10 > /dev/null

🎉 Final Thoughts

You've successfully completed urgent container service configuration! This is real-world DevOps operations:

What you accomplished:

  • ✅ Installed Apache2 in running container

  • ✅ Configured custom port 5002

  • ✅ Set Listen on ALL interfaces (0.0.0.0)

  • ✅ Started Apache service successfully

  • ✅ Verified accessibility from multiple sources

  • ✅ Kept container running and stable

Real-world impact:

  • Service configuration: Install and configure services in containers

  • Port management: Custom ports for multi-service deployments

  • Interface binding: Control network accessibility

  • Service persistence: Keep services running in containers

  • Troubleshooting: Debug and verify configurations

Key lessons:

  • apt-get works in Ubuntu/Debian containers

  • Port configuration requires both ports.conf and VirtualHost

  • Listen without IP = all interfaces

  • netstat/ss verify port binding

  • curl tests actual accessibility

  • Containers need foreground process to stay alive

This is production container management! You've handled an urgent task completion scenario! 💪

🚀 What's Next?

Day 40 complete! 🎉 You've mastered container service configuration!

Skills Mastered Today:

  • ✅ Installing services in containers

  • ✅ Apache configuration and port management

  • ✅ Service startup and verification

  • ✅ Network interface binding

  • ✅ Production troubleshooting

Coming up: More Docker adventures - Docker networking, volumes and persistence, multi-container applications!


Day: 40/100
Challenge: KodeKloud Cloud DevOps
Date: December 12, 2025
Topic: Docker Container Service Configuration

How do you manage services in containers? What's your configuration strategy? Share your container management practices! 🔧

More from this blog

🚀 DevOps Challenge- KodeKloud Solutions

73 posts