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! 🔧




