Master the process of creating, versioning, and deploying customized Nginx Docker images. You will learn how to build tailored containers, push them to an artifact registry like DockerHub, and seamlessly pull them for use in Docker, Kubernetes, or any Infrastructure as Code environment. This configuration-driven approach allows for rapid, repeatable deployments—simply update the image version tag in your manifests to reuse the same infrastructure logic across different environments.
INTRODUCkTION
Webapps- is a custom Nginx container, designed for training, testing, and demonstration purposes. It provides an engaging, visual way to uniquely identify running applications via their hostnames, IP addresses, and unique color themes based on each version.
Key Features & Attributes
Vibrant Visuals: Each version features a distinct color theme, making it easy to identify traffic shifts.
Version Switching: Easily manage and demonstrate seamless upgrades from one application version to another.
Training: Providing a simple, visual tool for new engineers to easily distinguish and track hosts, nodes, and containers in a live cluster.
Deployment Strategy Sandbox: Practice and validate core deployment patterns:
- Rolling Update
- Canary Deployment
- A/B Testing
- Shadow Deployment
- Blue-Green Deployment
- Recreate Strategy
Debugging Routing & Infrastructure Testing: Ideal for configuring and testing Load Balancers, Ingress Controllers, and AWS Auto Scaling Groups.Verifying Services are correctly mapping external requests to the right internal Pod identity.
Efficiencient, Simplified Code Reusability: To update or roll out a new version, you only need to modify the image tag in your configuration files, allowing for rapid, repeatable deployments without needing to rewrite your underlying infrastructure code. for instance
docker run -d -p 80:80 burgxy/webapps:v3
OR just change the version tag
docker run -d -p 80:80 burgxy/webapps:v6
- Full Workflow: Includes documented steps for building custom images and pushing them to an artifact registry.
Use Cases & Environments
This artifact is critical for achieving observability in complex, distributed systems like Kubernetes or Nomad. It moves us past simply knowing an app is ‘running’ to knowing exactly where the traffic is routed. Optimized for testing infrastructure routing and orchestration behavior in:
- Local Development: Docker, Minikube, Kind.
- Cloud Orchestration: Kubernetes (AWS EKS, Azure AKS, GCP GKE).
- Load Balancer Testing: Verifying traffic distribution and health check behaviors.
⚠️ Disclaimer - recommended solely for Testing, Training, Or Demonstration purposes
Warning: This container is strictly for lab use! It spills network secrets faster than your toxic ex-gf. Use it in production at your own risk; and definitely don’t blame me if things go sideways. Proceed with discretion.
Deployment Instructions
Forget the Dockerfile! I’ve done the heavy lifting and published more than four ready-to-pull images, in different themes and versions. With version tags like {v4, v3,v2 and v1}.
For more updates and latests versions, please refer to the dockerhub repository for Webapps here.
However, should you wish to execute any of your choice, follow the steps below :
v4
I think Version 4 is the clear winner. it’s Star Trek-themed and ready for warp speed! (Yes, it was AI enhanced LOL )
Engage V4:
docker run -d -p 80:80 burgxy/webapps:v4
v3
While Version 4 is great for high-drama space exploration, Version 3 is the smoothest, sharpest, and arguably the coolest build for a professional diagnostic check. It ditches the cosmic jargon for clean lines and clear data, making it the most highly recommended option for real-world demos.
You’re cleared to launch the recommended image, the one that always works:
docker run -d -p 80:80 burgxy/webapps:v3
This command will deploy the clean, blue-themed diagnostic page that focuses on clarity and stability. It’s the dependable workhorse of the fleet!
v2
This version does the job, but its logic was flawed (see Dockerfile and info.sh for further details)
docker run -d -p 80:80 burgxy/webapps:v2
v1
It was technically “fit for purpose,” but its original Dockerfile is now a classic training artifact on how not to manage PID 1. We publish it proudly to prove that the road to perfection starts with a very messy draft. A necessary, albeit flawed, step in the evolution!
docker run -d -p 80:80 burgxy/webapps:v1
Setup & Workflow
If you wish to re-create the steps to build and push your own modified container, this section provides sample dockerfiles and the workflow for your convenience samples of which are included below.
Building and Pushing Images
The simplfied proceedure is :
- Build:
docker build -t <your-registry>/nginx-demo:<version> . - Push:
docker push <your-registry>/nginx-demo:<version>
My Steps and proceedures are :
Step 1: Build the Image Locally
Assuming you got a Dockerfile: (let’s say version 2 from below). to build the image you execute
docker build . -t burgxy/webapps:v2
Step 2: Log in & Update Docker Hub Repository
(Optional: if pushing to Docker Hub)
docker login
docker push burgxy/webapps:v2
Step 3: Run Container from any Docker Host (Port 80)
Please select your desired deployment method below.
Step 4: Run in Docker
docker run -d -p 80:80 burgxy/webapps:v2
Or in Kubernetes
Simple Pod
kubectl run webapp --image=burgxy/webapps:v2 --port=80
Deployment & Service
kubectl create deployment jj-app --image=burgxy/webapps:v2
#next, we create and expose the service
kubectl expose deployment jj-app --type=NodePort --port=80
kubectl get svc jj-app
Replica & LoadBalancer
kubectl create deployment jj-app --image=burgxy/webapps:v2 --replicas=3
kubectl expose deployment jj-app --type=LoadBalancer --port=80
kubectl get svc jj-app
Code & Versions
Explore the evolution of this container. Below are the Dockerfiles and supporting scripts for each version.
Version: v4 - (Star Trek Theme)
Dockerfile
FROM nginx:latest
#Copy the script and give it execute permissions
COPY info.sh /tmp/
RUN chmod +x /tmp/info.sh
#Use the info.sh script as the ENTRYPOINT
ENTRYPOINT [ "/tmp/info.sh" ]
CMD []
info.sh
#!/bin/bash
set -e
# Function to generate and refresh the HTML content
update_index() {
while true
do
# Capture dynamic system variables
HM=$(hostname)
IP=$(hostname -I | awk '{print $1}')
DATE=$(date)
# Generate the full HTML structure with inline CSS for aesthetics
cat <<EOF > /usr/share/nginx/html/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LCARS: USS JayJay Operations</title>
<style>
/* LCARS Base Styling */
body {
font-family: 'Inter', sans-serif;
background-color: #000033; /* Deep space blue/black */
color: #ffcc00; /* Gold text */
text-align: center;
padding: 20px;
position: relative;
min-height: 100vh;
}
.container {
max-width: 800px;
margin: 50px auto;
background-color: #111166; /* Main LCARS block background */
border-radius: 12px;
box-shadow: 0 0 30px rgba(0, 100, 255, 0.5); /* Blue glow */
padding: 40px;
border: 4px solid #ffcc00; /* Gold trim */
}
h1 {
color: #ff9999; /* Pink/coral LCARS accent */
font-size: 2.8em;
margin-bottom: 5px;
text-shadow: 0 0 5px #ffcc00;
}
.subtitle {
font-size: 1.5em;
color: #cc9900; /* Subtle gold */
margin-bottom: 30px;
font-style: italic;
border-bottom: 2px solid #3366cc;
padding-bottom: 10px;
}
/* System Data Box - LCARS Panel */
.data-box {
border: 2px solid #3366cc;
padding: 20px;
background-color: #000044;
border-radius: 8px;
margin-top: 30px;
text-align: left;
box-shadow: inset 0 0 10px rgba(51, 102, 204, 0.5);
}
.data-line {
margin: 15px 0;
font-size: 1.2em;
color: #99ccff; /* Light blue info text */
}
.data-line strong {
display: inline-block;
width: 250px; /* Wider for long labels */
color: #ff9999; /* Pink label */
font-weight: bold;
}
.footer {
margin-top: 40px;
font-size: 0.9em;
color: #7f8c8d;
}
/* Disclaimer - Red Alert */
.disclaimer {
color: #ff0000;
font-weight: bold;
margin-top: 30px;
border: 3px solid #ff0000;
padding: 15px;
border-radius: 8px;
background-color: rgba(255, 0, 0, 0.1);
animation: pulse 1.5s infinite alternate;
}
/* Trendy Bottom-Right Link - Communicator Badge */
.credit {
position: fixed;
bottom: 20px;
right: 20px;
padding: 10px 15px;
background-color: #3366cc; /* LCARS Blue */
color: white;
text-decoration: none;
border-radius: 12px;
font-size: 1.1em;
font-weight: 600;
transition: all 0.2s;
z-index: 100;
box-shadow: 0 4px 15px rgba(51, 102, 204, 0.8);
}
.credit:hover {
background-color: #ffcc00; /* Gold hover */
color: #111166;
transform: scale(1.1) rotate(-2deg);
}
@keyframes pulse {
from { box-shadow: 0 0 10px #ff0000; }
to { box-shadow: 0 0 20px #ff0000, 0 0 5px #ff0000; }
}
</style>
</head>
<body>
<div class="container">
<h1>Starfleet Operations: LCARS ONLINE</h1>
<p class="subtitle">Captain's Log: Initial diagnostics complete. All systems nominal. Proceed to Warp Speed! 🚀</p>
<div class="data-box">
<div class="data-line"><strong>Stardate Chronometer:</strong> <code>$DATE</code></div>
<div class="data-line"><strong>Vessel Designation (NCC):</strong> <code>$HM</code></div>
<div class="data-line"><strong>Tactical IP Signature:</strong> <code>$IP</code></div>
</div>
<div class="footer">
<p>Infrastructure Note: Propulsion system proudly powered by an auxiliary Nginx Warp Core. Engaged! 🫡</p>
<p>(Sensor data refresh cycle: 60 Ticks.)</p>
</div>
<!-- Disclaimer Inclusion - Red Alert -->
<p class="disclaimer">🚨 **RED ALERT - NON-CANON TEST VESSEL:** This container is strictly for simulation and testing. Deploying this vessel into the Gamma Quadrant (production) is highly illogical and prohibited by Starfleet Regulation 42. 🚨</p>
</div>
<!-- JJ Ochinyabo LinkedIn Link - Communicator Badge -->
<a href="https://www.linkedin.com/in/jeremiah-j-ochinyabo-77b83aa7/" target="_blank" class="credit">
<span style="font-size:1.5em; margin-right: 5px;">🔭</span> Contact Chief Engineer JJ
</a>
</body>
</html>
EOF
# Sleep for 60 seconds before the next update
sleep 60
done
}
# 1. Start the content updating function in the background
update_index &
# 2. Execute the main application (Nginx) in the foreground
# The 'exec' command is critical here to ensure Nginx takes PID 1
# so Docker can manage the process correctly.
echo "Starting Nginx as the primary service in the foreground..."
exec nginx -g "daemon off;"
Version: v3 (Fun, but the recommended version )
Dockerfile
FROM nginx:latest
#Copy the script and give it execute permissions
COPY info.sh /tmp/
RUN chmod +x /tmp/info.sh
#Use the info.sh script as the ENTRYPOINT
ENTRYPOINT [ "/tmp/info.sh" ]
CMD []
info.sh
#!/bin/bash
set -e
# Function to generate and refresh the HTML content
update_index() {
while true
do
# Capture dynamic system variables
HM=$(hostname)
IP=$(hostname -I | awk '{print $1}')
DATE=$(date)
# Generate the full HTML structure with inline CSS for aesthetics
cat <<EOF > /usr/share/nginx/html/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JayJay: The Working App</title>
<style>
/* Base styling */
body { font-family: 'Inter', sans-serif; background-color: #e6f7ff; color: #2c3e50; text-align: center; padding: 20px; position: relative; min-height: 100vh; }
.container { max-width: 650px; margin: 50px auto; background-color: #ffffff; border-radius: 16px; box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2); padding: 40px; border: 4px solid #007bff; }
h1 { color: #007bff; font-size: 2.5em; margin-bottom: 10px; }
.subtitle { font-size: 1.3em; color: #1e83f0; margin-bottom: 30px; font-style: italic; }
/* System Data Box */
.data-box { border: 2px dashed #a0c4ff; padding: 20px; background-color: #f7fbff; border-radius: 10px; margin-top: 30px; text-align: left; }
.data-line { margin: 10px 0; font-size: 1.1em; }
.data-line strong { display: inline-block; width: 180px; color: #34495e; font-weight: bold; }
.footer { margin-top: 40px; font-size: 0.9em; color: #7f8c8d; }
/* New Disclaimer Style */
.disclaimer { color: #e74c3c; font-weight: bold; margin-top: 30px; border: 1px solid #e74c3c; padding: 10px; border-radius: 8px; background-color: #fdeaea; }
/* New Trendy Bottom-Right Link Style */
.credit {
position: fixed;
bottom: 20px;
right: 20px;
padding: 10px 15px;
background-color: #3498db;
color: white;
text-decoration: none;
border-radius: 12px;
font-size: 1em;
font-weight: 600;
transition: transform 0.2s, background-color 0.2s;
z-index: 100;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.credit:hover {
background-color: #2980b9;
transform: scale(1.05);
}
</style>
</head>
<body>
<div class="container">
<h1> Hi👋 I'm JayJay 😃, your App Host! </h1>
<p class="subtitle">If you're seeing this, it means I'm not just working... I'm *thriving*! 🥳</p>
<div class="data-box">
<div class="data-line"><strong>Time Traveler Log:</strong> <code>$DATE</code></div>
<div class="data-line"><strong>Pod/Container ID:</strong> <code>$HM</code></div>
<div class="data-line"><strong>Local IP Address:</strong> <code>$IP</code></div>
</div>
<div class="footer">
<p>Infrastructure Note: Proudly served by the mighty Nginx Web Server. Thank you, Nginx! 🫡</p>
<p>(This data refreshes automatically every 60 seconds.)</p>
</div>
<!-- Disclaimer Inclusion -->
<p class="disclaimer">⚠️ **DISCLAIMER:** This container is for testing and demonstration purposes ONLY. Do not run in a production environment. ⚠️</p>
</div>
<!-- JJ Ochinyabo LinkedIn Link -->
<a href="https://www.linkedin.com/in/jeremiah-j-ochinyabo-77b83aa7/" target="_blank" class="credit">
<span style="font-size:1.5em; margin-right: 5px;">😎</span> Find JJ Ochinyabo Here!
</a>
</body>
</html>
EOF
# Sleep for 60 seconds before the next update
sleep 60
done
}
# 1. Start the content updating function in the background
update_index &
# 2. Execute the main application (Nginx) in the foreground
# The 'exec' command is critical here to ensure Nginx takes PID 1
# so Docker can manage the process correctly.
echo "Starting Nginx as the primary service in the foreground..."
exec nginx -g "daemon off;"
Version: v2 (It works.. but Flawed Build)
Dockerfile
FROM nginx:latest
COPY info.sh /tmp/
RUN chmod +x /tmp/info.sh && nginx -c /etc/nginx/nginx.conf && echo "daemon off;" >> /etc/nginx/nginx.conf
WORKDIR /tmp
ENTRYPOINT [ "/bin/bash", "-c", "/tmp/info.sh" ]
info.sh
#! /bin/bash
set -e
while true
do
HM=$(hostname)
IP=$(hostname -I)
DATE=$(date)
echo -e "" >> /usr/share/nginx/html/index.html
echo -e "" >> /usr/share/nginx/html/index.html
echo -e "++++++++++++++++++++++++++++++++++++++++++++++++++" >> /usr/share/nginx/html/index.html
echo -e "" >> /usr/share/nginx/html/index.html
echo -e " DATE : $DATE " >> /usr/share/nginx/html/index.html
echo -e "" >> /usr/share/nginx/html/index.html
echo -e "++++++++++++++++++++++++++++++++++++++++++++++++++" >> /usr/share/nginx/html/index.html
echo -e "" >> /usr/share/nginx/html/index.html
echo -e "\n CONTAINER NAME : $HM " >> /usr/share/nginx/html/index.html
echo -e "+++++++............................................................+++++++\n" >> /usr/share/nginx/html/index.html
echo -e "\n LOCAL IP : $IP" >> /usr/share/nginx/html/index.html
echo -e "\n" >> /usr/share/nginx/html/index.html
echo -e "++++++++++++++++++++++++++++++++++++++++++++++++++" >> /usr/share/nginx/html/index.html
sed -i -e 's/nginx!/JayJay-Demo-System/g' /usr/share/nginx/html/index.html
nginx
sleep 500
echo -e "" > /usr/share/nginx/html/index.html
done
Version: v1 (the first concept)
Dockerfile
Dockerfile
FROM nginx:latest
CMD ["sh", "-c", "hostname -I >> /usr/share/nginx/html/index.html & nginx -g 'daemon off;'"]
info.sh
NONE required.