<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: alejiri</title>
    <description>The latest articles on DEV Community by alejiri (@alejiri).</description>
    <link>https://dev.to/alejiri</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3568738%2Fc770b0e9-f653-4f3f-9957-a91000e64c4d.jpeg</url>
      <title>DEV Community: alejiri</title>
      <link>https://dev.to/alejiri</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alejiri"/>
    <language>en</language>
    <item>
      <title>Docker NGINX + WordPress + MariaDB Tutorial - Inception42</title>
      <dc:creator>alejiri</dc:creator>
      <pubDate>Thu, 16 Oct 2025 11:14:27 +0000</pubDate>
      <link>https://dev.to/alejiri/docker-nginx-wordpress-mariadb-tutorial-inception42-1eok</link>
      <guid>https://dev.to/alejiri/docker-nginx-wordpress-mariadb-tutorial-inception42-1eok</guid>
      <description>&lt;h2&gt;
  
  
  A Complete Step-by-Step Deep Dive MVP Tutorial
&lt;/h2&gt;

&lt;p&gt;As part of my coding learning journey at 42Berlin, I created this tutorial to share my experience with my fellow students and others who might benefit from it.&lt;/p&gt;

&lt;p&gt;This tutorial follows the Inception project specifications and will guide you through the basic Docker concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You'll Build
&lt;/h3&gt;

&lt;p&gt;By the end of this tutorial, you'll have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ NGINX reverse proxy with TLS encryption&lt;/li&gt;
&lt;li&gt;✅ WordPress with PHP-FPM&lt;/li&gt;
&lt;li&gt;✅ MariaDB database&lt;/li&gt;
&lt;li&gt;✅ Docker networking and volumes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 1: Understanding the Fundamentals
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Docker?
&lt;/h3&gt;

&lt;p&gt;Think of Docker as a mini-virtual machine that can run one app or service. This is what we call container. The idea is to have every app running in one container. For every container executing an app we have a recipe, the Dockerfile. And the recipe to coordinate several containers is called Docker compose.&lt;/p&gt;

&lt;p&gt;Docker containers package your application with everything it needs to run consistently across different environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reflection Question&lt;/strong&gt;: If you had to explain Docker to a non-technical friend in one sentence, what would you say?&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker vs Virtual Machines
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Docker Containers&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Virtual Machines&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Share host OS kernel&lt;/td&gt;
&lt;td&gt;Each VM has full OS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Startup Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Seconds&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MB&lt;/td&gt;
&lt;td&gt;GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Portability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Think About It&lt;/strong&gt;: Why would you choose containers over VMs for a web application?&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: Setting Up Your Environment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Virtual Machine (Ubuntu 22.04 or 24.04 recommended) with Root or sudo access.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installing Docker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update package index&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update

&lt;span class="c"&gt;# Install required packages&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;apt-transport-https ca-certificates curl gnupg lsb-release

&lt;span class="c"&gt;# Add Docker's GPG key&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/share/keyrings/docker-archive-keyring.gpg

&lt;span class="c"&gt;# Add Docker repository&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

&lt;span class="c"&gt;# Install Docker&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce docker-ce-cli containerd.io docker-compose-plugin

&lt;span class="c"&gt;# Add your user to docker group&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;

&lt;span class="c"&gt;# Log out and back in, then test&lt;/span&gt;
docker &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Experiment&lt;/strong&gt;: Run &lt;code&gt;docker run hello-world&lt;/code&gt;. What do you think this command does?&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: Docker Basics - Building Blocks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding Key Concepts
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Images vs Containers
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List images&lt;/span&gt;
docker images

&lt;span class="c"&gt;# List running containers&lt;/span&gt;
docker ps

&lt;span class="c"&gt;# List all containers (including stopped)&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt;: What's the difference between an image and a container? Think of it like the difference between a recipe and a cake!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question 2&lt;/strong&gt;: Is a stopped container the same as a Docker image?&lt;/p&gt;

&lt;h4&gt;
  
  
  Your First Container
&lt;/h4&gt;

&lt;p&gt;To make sure that you can experience the power of Docker soon, you can follow this exercise that is using a nginx pre-made image (forbiden for inception but good for Docker understanding)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run a simple nginx container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;--name&lt;/span&gt; my-nginx nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What do you think will happen if you run this command?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What does &lt;code&gt;-d&lt;/code&gt; do?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;-p 8080:80&lt;/code&gt; mean?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;--name my-nginx&lt;/code&gt; do?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visit &lt;code&gt;http://localhost:8080&lt;/code&gt; to see the result!&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4: Creating Your First Dockerfile
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a Dockerfile?
&lt;/h3&gt;

&lt;p&gt;A Dockerfile is like a recipe that tells Docker how to build your image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Simple nginx Dockerfile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.html /usr/share/nginx/html/&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking it down&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FROM&lt;/code&gt;: What base image to start with&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COPY&lt;/code&gt;: Copy files from host to container (same folder that the Dockerfile)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EXPOSE&lt;/code&gt;: Document which port the container uses (just document!)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Building Your Image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build the image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-custom-nginx &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Run your custom image&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 my-custom-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Experiment&lt;/strong&gt;: Create an &lt;code&gt;index.html&lt;/code&gt; file with your name and build the image. What happens?&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5: The Inception Project Architecture
&lt;/h2&gt;

&lt;p&gt;Before we dive into building, let's understand what we're creating:&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inception/
├── Makefile
├── secrets/
│   ├── db_password.txt
│   └── db_root_password.txt
└── srcs/
    ├── docker-compose.yml
    ├── .env
    └── requirements/
        ├── mariadb/
        │   ├── Dockerfile
        │   ├── conf/
        │   └── tools/
        ├── nginx/
        │   ├── Dockerfile
        │   ├── conf/
        │   └── tools/
        └── wordpress/
            ├── Dockerfile
            ├── conf/
            └── tools/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reflection&lt;/strong&gt;: Why do you think we separate each service into its own directory?&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 6: Setting Up Docker Networks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding Docker Networking
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a custom network&lt;/span&gt;
docker network create inception-network

&lt;span class="c"&gt;# List networks&lt;/span&gt;
docker network &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# Inspect the network&lt;/span&gt;
docker network inspect inception-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Think&lt;/strong&gt;: Why can't we just use the default network?&lt;/p&gt;

&lt;p&gt;The default bridge network has limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Containers can only communicate by IP address&lt;/li&gt;
&lt;li&gt;No automatic DNS resolution&lt;/li&gt;
&lt;li&gt;Less secure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Experiment&lt;/strong&gt;: Create two containers on the same custom network and try to ping one from the other using container names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Connect a running container&lt;/span&gt;
docker network connect inception-network &amp;lt;container_id_or_name&amp;gt;

&lt;span class="c"&gt;# Create a httpd apache server connected to the network&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; client &lt;span class="nt"&gt;--network&lt;/span&gt; inception-network &lt;span class="nt"&gt;-d&lt;/span&gt; httpd

&lt;span class="c"&gt;# To enter shell&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; client sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# If ping is missing&lt;/span&gt;
apk add iputils    &lt;span class="c"&gt;# For Alpine&lt;/span&gt;
apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install &lt;/span&gt;iputils-ping   &lt;span class="c"&gt;# For Debian/Ubuntu&lt;/span&gt;

&lt;span class="c"&gt;# To get an IP address&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'&lt;/span&gt; &amp;lt;container_id_or_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 7: MariaDB Container - The Database Layer
&lt;/h2&gt;

&lt;p&gt;Now is time to start building Inception. You can move from a fresh folder to save your final work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the MariaDB Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; debian:bookworm&lt;/span&gt;

&lt;span class="c"&gt;# Install MariaDB&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    mariadb-server &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Create directories&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/run/mysqld &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; mysql:mysql /var/run/mysqld &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;755 /var/run/mysqld

&lt;span class="c"&gt;# Copy configuration&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; conf/50-server.cnf /etc/mysql/mariadb.conf.d/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; tools/init_db.sh /usr/local/bin/&lt;/span&gt;

&lt;span class="c"&gt;# Set permissions&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/init_db.sh

&lt;span class="c"&gt;# Environment variables (can be overridden at runtime)&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; MYSQL_ROOT_PASSWORD=root \&lt;/span&gt;
    MYSQL_DATABASE=app_db \
    MYSQL_USER=app_user \
    MYSQL_PASSWORD=app_pass

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3306&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["init_db.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Questions to ponder&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why are we creating &lt;code&gt;/var/run/mysqld&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;chown&lt;/code&gt; do?&lt;/li&gt;
&lt;li&gt;Why do we need an initialization script?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  MariaDB Configuration File
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;conf/50-server.cnf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mysqld]&lt;/span&gt;
&lt;span class="py"&gt;bind-address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0&lt;/span&gt;
&lt;span class="py"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3306&lt;/span&gt;
&lt;span class="py"&gt;socket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/run/mysqld/mysqld.sock&lt;/span&gt;
&lt;span class="py"&gt;datadir&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/lib/mysql&lt;/span&gt;
&lt;span class="py"&gt;log-error&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/log/mysql/error.log&lt;/span&gt;
&lt;span class="py"&gt;pid-file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/run/mysqld/mysqld.pid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical thinking&lt;/strong&gt;: Why is &lt;code&gt;bind-address = 0.0.0.0&lt;/code&gt; important here?&lt;/p&gt;

&lt;h3&gt;
  
  
  Database Initialization Script
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;tools/init_db.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting MariaDB initialization..."&lt;/span&gt;

&lt;span class="c"&gt;# Initialize MySQL data directory if it doesn't exist&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"/var/lib/mysql/mysql"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Initializing data directory..."&lt;/span&gt;
    mysql_install_db &lt;span class="nt"&gt;--user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql &lt;span class="nt"&gt;--datadir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/mysql &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Start the server (no networking for setup)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting temporary MariaDB server for setup..."&lt;/span&gt;
mysqld &lt;span class="nt"&gt;--skip-networking&lt;/span&gt; &lt;span class="nt"&gt;--socket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/mysqld/mysqld.sock &lt;span class="nt"&gt;--user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql &amp;amp;
&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$!&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Wait for MariaDB to be ready&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Waiting for MariaDB to be ready..."&lt;/span&gt;
&lt;span class="k"&gt;until &lt;/span&gt;mysqladmin &lt;span class="nt"&gt;--socket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/mysqld/mysqld.sock ping &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;1
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MariaDB is ready!"&lt;/span&gt;

&lt;span class="c"&gt;# Run setup SQL: create database and users&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running setup SQL..."&lt;/span&gt;
mysql &lt;span class="nt"&gt;--socket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/mysqld/mysqld.sock &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
ALTER USER 'root'@'localhost' IDENTIFIED BY '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;';
CREATE DATABASE IF NOT EXISTS &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;;
CREATE USER IF NOT EXISTS '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'@'%' IDENTIFIED BY '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;';
GRANT ALL PRIVILEGES ON &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;.* TO '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'@'%';
FLUSH PRIVILEGES;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Shut down temporary server&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Shutting down temporary MariaDB..."&lt;/span&gt;
mysqladmin &lt;span class="nt"&gt;--socket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/mysqld/mysqld.sock &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; shutdown

&lt;span class="c"&gt;# Wait for shutdown&lt;/span&gt;
&lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Start MariaDB normally (with networking)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Initialization complete. Starting MariaDB..."&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;mysqld &lt;span class="nt"&gt;--user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql &lt;span class="nt"&gt;--datadir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/mysql &lt;span class="nt"&gt;--socket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/mysqld/mysqld.sock

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Experiment Question&lt;/strong&gt;: What would happen if we didn't wait for MySQL to start before creating the database?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# To check if socket file exist on container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;container_id_or_name&amp;gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /run/mysqld/mysqld.sock

&lt;span class="c"&gt;# To check maria db&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;container_id_or_name&amp;gt; mysqladmin ping &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 8: WordPress with PHP-FPM Container
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding PHP-FPM
&lt;/h3&gt;

&lt;p&gt;PHP-FPM (FastCGI Process Manager) is a PHP implementation that's perfect for serving high-traffic sites. Unlike running PHP as an Apache module, PHP-FPM runs as a separate process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use PHP-FPM instead of Apache with mod_php?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better performance under high load&lt;/li&gt;
&lt;li&gt;Separate process isolation&lt;/li&gt;
&lt;li&gt;Better resource management&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  WordPress Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; debian:bookworm&lt;/span&gt;

&lt;span class="c"&gt;# Install PHP-FPM and required extensions&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  php8.2-fpm &lt;span class="se"&gt;\
&lt;/span&gt;  php8.2-mysql &lt;span class="se"&gt;\
&lt;/span&gt;  php8.2-curl &lt;span class="se"&gt;\
&lt;/span&gt;  php8.2-gd &lt;span class="se"&gt;\
&lt;/span&gt;  php8.2-intl &lt;span class="se"&gt;\
&lt;/span&gt;  php8.2-mbstring &lt;span class="se"&gt;\
&lt;/span&gt;  php8.2-soap &lt;span class="se"&gt;\
&lt;/span&gt;  php8.2-xml &lt;span class="se"&gt;\
&lt;/span&gt;  php8.2-zip &lt;span class="se"&gt;\
&lt;/span&gt;  wget &lt;span class="se"&gt;\
&lt;/span&gt;  curl &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /usr/sbin/php-fpm8.2 /usr/local/bin/php-fpm &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Create WordPress directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /var/www/html&lt;/span&gt;

&lt;span class="c"&gt;# Copy PHP-FPM configuration (correct version)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; conf/www.conf /etc/php/8.2/fpm/pool.d/&lt;/span&gt;

&lt;span class="c"&gt;# Copy and set permissions for setup script&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; tools/setup_wordpress.sh /usr/local/bin/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/setup_wordpress.sh

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 9000&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/usr/local/bin/setup_wordpress.sh"]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reflection&lt;/strong&gt;: Why do we need so many PHP extensions?&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP-FPM Configuration
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;conf/www.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[www]&lt;/span&gt;
&lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
&lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
&lt;span class="py"&gt;listen&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;9000&lt;/span&gt;
&lt;span class="py"&gt;listen.owner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
&lt;span class="py"&gt;listen.group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
&lt;span class="py"&gt;pm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;dynamic&lt;/span&gt;
&lt;span class="py"&gt;pm.max_children&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;5&lt;/span&gt;
&lt;span class="py"&gt;pm.start_servers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2&lt;/span&gt;
&lt;span class="py"&gt;pm.min_spare_servers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="py"&gt;pm.max_spare_servers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Challenge Question&lt;/strong&gt;: What would happen if you set &lt;code&gt;pm.max_children = 1&lt;/code&gt; on a busy website?&lt;/p&gt;

&lt;h3&gt;
  
  
  WordPress Setup Script
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;tools/setup_wordpress.sh&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Note: If the variable is &lt;strong&gt;only used by the script itself&lt;/strong&gt;, no need for &lt;code&gt;export&lt;/code&gt;. If it must be &lt;strong&gt;seen by another process&lt;/strong&gt; (e.g., PHP, nginx, Python, etc.), then you should &lt;code&gt;export&lt;/code&gt; it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;WP_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/www/html"&lt;/span&gt;

&lt;span class="c"&gt;# Read password from secret file&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WORDPRESS_DB_PASSWORD_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WORDPRESS_DB_PASSWORD_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;WORDPRESS_DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WORDPRESS_DB_PASSWORD_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;export &lt;/span&gt;WORDPRESS_DB_PASSWORD
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Setting up WordPress..."&lt;/span&gt;

&lt;span class="c"&gt;# Download and configure WordPress if not present&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WP_PATH&lt;/span&gt;&lt;span class="s2"&gt;/wp-config.php"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Downloading WordPress..."&lt;/span&gt;
    wget &lt;span class="nt"&gt;-q&lt;/span&gt; https://wordpress.org/latest.tar.gz &lt;span class="nt"&gt;-O&lt;/span&gt; /tmp/wordpress.tar.gz
    &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; /tmp/wordpress.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; /tmp
    &lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/wordpress.tar.gz

    &lt;span class="c"&gt;# Copy only missing files (avoid overwriting existing content)&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; /tmp/wordpress/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WP_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
    rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/wordpress

    &lt;span class="c"&gt;# Fetch security salts from WordPress API&lt;/span&gt;
    &lt;span class="nv"&gt;WP_SALTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; https://api.wordpress.org/secret-key/1.1/salt/&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Create wp-config.php&lt;/span&gt;
    &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WP_PATH&lt;/span&gt;&lt;span class="s2"&gt;/wp-config.php"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
&amp;lt;?php
define('DB_NAME', '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORDPRESS_DB_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;');
define('DB_USER', '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;');
define('DB_PASSWORD', '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORDPRESS_DB_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;');
define('DB_HOST', '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORDPRESS_DB_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');

&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;table_prefix = '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORDPRESS_TABLE_PREFIX&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;wp_&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;';

&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WP_SALTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;

define('WP_DEBUG', false);

if ( !defined('ABSPATH') )
    define('ABSPATH', __DIR__ . '/');

require_once ABSPATH . 'wp-settings.php';
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;    &lt;span class="c"&gt;# Set secure permissions&lt;/span&gt;
    find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WP_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;750 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
    find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WP_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;640 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
    &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data:www-data &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WP_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"WordPress setup complete."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"WordPress already initialized, skipping setup."&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting PHP-FPM..."&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;php-fpm8.2 &lt;span class="nt"&gt;-F&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Experiment&lt;/strong&gt;: What do you think happens if WordPress files already exist?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="c"&gt;# If we want to test wp container we can use this:&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;WORDPRESS_DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dummyhost &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;WORDPRESS_DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dummydb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dummyuser &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;WORDPRESS_DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dummypassword &lt;span class="se"&gt;\&lt;/span&gt;
  mywp

&lt;span class="c"&gt;# Then&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; docker_id_name &lt;span class="nb"&gt;cat&lt;/span&gt; /var/www/html/wp-config.php


&lt;span class="c"&gt;# And then check with:&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /var/www/html/wp-config.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 9: NGINX with TLS - The Front Door
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding NGINX's Role
&lt;/h3&gt;

&lt;p&gt;NGINX acts as a reverse proxy, meaning it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receives requests from users&lt;/li&gt;
&lt;li&gt;Forwards them to PHP-FPM&lt;/li&gt;
&lt;li&gt;Returns the response to users&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why not access PHP-FPM directly?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP-FPM doesn't handle HTTP directly&lt;/li&gt;
&lt;li&gt;NGINX handles static files efficiently&lt;/li&gt;
&lt;li&gt;SSL termination&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  NGINX Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; debian:bullseye&lt;/span&gt;

&lt;span class="c"&gt;# Install nginx and openssl&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    nginx &lt;span class="se"&gt;\
&lt;/span&gt;    openssl &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Ensure nginx runs as www-data with correct UID&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;usermod &lt;span class="nt"&gt;-u&lt;/span&gt; 33 www-data &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; groupmod &lt;span class="nt"&gt;-g&lt;/span&gt; 33 www-data

&lt;span class="c"&gt;# Create SSL directory&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/nginx/ssl

&lt;span class="c"&gt;# Copy configuration files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; conf/nginx.conf /etc/nginx/nginx.conf&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; tools/generate_ssl.sh /usr/local/bin/&lt;/span&gt;

&lt;span class="c"&gt;# Set permissions&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/&lt;span class="k"&gt;*&lt;/span&gt;.sh

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 443&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/usr/local/bin/generate_ssl.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSL Certificate Generation Script
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;tools/generate_ssl.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Ensure SSL directory exists&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/nginx/ssl

&lt;span class="c"&gt;# Default domain if not provided&lt;/span&gt;
: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOMAIN_NAME&lt;/span&gt;:&lt;span class="p"&gt;=localhost&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Generate SSL certificate if it doesn't exist&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/nginx/ssl/nginx.crt &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating self-signed SSL certificate for &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOMAIN_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;

    openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-keyout&lt;/span&gt; /etc/nginx/ssl/nginx.key &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-out&lt;/span&gt; /etc/nginx/ssl/nginx.crt &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=US/ST=State/L=City/O=Organization/CN=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOMAIN_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;chmod &lt;/span&gt;600 /etc/nginx/ssl/nginx.key
    &lt;span class="nb"&gt;chmod &lt;/span&gt;644 /etc/nginx/ssl/nginx.crt

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SSL certificate generated at /etc/nginx/ssl/"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SSL certificate already exists. Skipping generation."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Test nginx configuration before starting&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Testing nginx configuration..."&lt;/span&gt;
nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Nginx configuration test passed."&lt;/span&gt;

&lt;span class="c"&gt;# Start nginx in foreground (PID 1)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting Nginx..."&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;nginx &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="s2"&gt;"daemon off;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Security Question&lt;/strong&gt;: Why do we set different permissions for the key and certificate files?&lt;/p&gt;

&lt;h3&gt;
  
  
  NGINX Configuration
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;conf/nginx.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;worker_connections&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/mime.types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;default_type&lt;/span&gt; &lt;span class="nc"&gt;application/octet-stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;server_name&lt;/span&gt; $&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kn"&gt;DOMAIN_NAME&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;server_name&lt;/span&gt; $&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kn"&gt;DOMAIN_NAME&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/ssl/nginx.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/ssl/nginx.key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_protocols&lt;/span&gt; &lt;span class="s"&gt;TLSv1.2&lt;/span&gt; &lt;span class="s"&gt;TLSv1.3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;ssl_ciphers&lt;/span&gt; &lt;span class="s"&gt;ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.php&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;client_max_body_size&lt;/span&gt; &lt;span class="mi"&gt;64M&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# Security headers&lt;/span&gt;
        &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Frame-Options&lt;/span&gt; &lt;span class="s"&gt;"SAMEORIGIN"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Content-Type-Options&lt;/span&gt; &lt;span class="s"&gt;"nosniff"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-XSS-Protection&lt;/span&gt; &lt;span class="s"&gt;"1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kn"&gt;mode=block"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.php?&lt;/span&gt;&lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;\.php$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;fastcgi_pass&lt;/span&gt; &lt;span class="nf"&gt;wordpress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;fastcgi_index&lt;/span&gt; &lt;span class="s"&gt;index.php&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;fastcgi_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;fastcgi_param&lt;/span&gt; &lt;span class="s"&gt;SCRIPT_FILENAME&lt;/span&gt; &lt;span class="nv"&gt;$document_root$fastcgi_script_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;/\.ht&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;.(js|css|png|jpg|jpeg|gif|ico|svg)&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="mi"&gt;1M&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt; &lt;span class="s"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical Analysis&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why does &lt;code&gt;fastcgi_pass&lt;/code&gt; use &lt;code&gt;wordpress:9000&lt;/code&gt; instead of an IP address?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;try_files&lt;/code&gt; do?
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check certificate&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; docker_id_name &lt;span class="nb"&gt;ls&lt;/span&gt; /etc/nginx/ssl/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 10: Docker Volumes - Data Persistence
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding Docker Volumes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happens to data when a container is deleted?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without volumes, all data is lost! Volumes provide persistent storage that survives container restarts and deletions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create volumes&lt;/span&gt;
docker volume create mariadb_data
docker volume create wordpress_data

&lt;span class="c"&gt;# Inspect a volume&lt;/span&gt;
docker volume inspect mariadb_data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Think&lt;/strong&gt;: Where does Docker store volume data on the host?&lt;/p&gt;

&lt;h3&gt;
  
  
  Volume Types Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Portability&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Named Volumes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Databases, app data&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bind Mounts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Development, logs&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tmpfs Mounts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Temporary data&lt;/td&gt;
&lt;td&gt;Highest&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Part 11: Environment Variables and Secrets
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Security Challenge
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;NEVER do this in production:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; MYSQL_ROOT_PASSWORD=supersecret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Instead, use environment variables:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DOMAIN_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;login.42.fr
&lt;span class="nv"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secure_root_password
&lt;span class="nv"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wordpress_db
&lt;span class="nv"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wp_user
&lt;span class="nv"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secure_user_password

&lt;span class="c"&gt;# WordPress settings&lt;/span&gt;
&lt;span class="nv"&gt;WORDPRESS_DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wordpress_db
&lt;span class="nv"&gt;WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wp_user
&lt;span class="nv"&gt;WORDPRESS_DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secure_user_password
&lt;span class="nv"&gt;WORDPRESS_DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mariadb
&lt;span class="nv"&gt;WORDPRESS_TABLE_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wp_
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Security Best Practice&lt;/strong&gt;: Store passwords in separate files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create secrets directory&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; secrets
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"root"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; secrets/db_root_password.txt
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"pass"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; secrets/db_password.txt

&lt;span class="c"&gt;# Set strict permissions&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 secrets/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Security Question&lt;/strong&gt;: Why are environment variables not ideal for secrets?&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 12: Docker Compose - Orchestrating Everything
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Docker Compose?
&lt;/h3&gt;

&lt;p&gt;Docker Compose allows you to define and run multi-container applications using a YAML file.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;srcs/docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mariadb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;requirements/mariadb&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mariadb:inception&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mariadb&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;inception-network&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mariadb_data:/var/lib/mysql&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/run/secrets/db_root_password&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MYSQL_DATABASE}&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MYSQL_USER}&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_PASSWORD_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/run/secrets/db_password&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_root_password&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_password&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;

  &lt;span class="na"&gt;wordpress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;requirements/wordpress&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress:inception&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mariadb&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;inception-network&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wordpress_data:/var/www/html&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mariadb&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MYSQL_DATABASE}&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MYSQL_USER}&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_PASSWORD_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/run/secrets/db_password&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_TABLE_PREFIX&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wp_&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_password&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;

  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;requirements/nginx&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:inception&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;inception-network&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wordpress_data:/var/www/html&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DOMAIN_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DOMAIN_NAME}&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;inception-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mariadb_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
    &lt;span class="na"&gt;driver_opts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;
      &lt;span class="na"&gt;o&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind&lt;/span&gt;
      &lt;span class="na"&gt;device&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/${USER}/data/mariadb&lt;/span&gt;
  &lt;span class="na"&gt;wordpress_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
    &lt;span class="na"&gt;driver_opts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;
      &lt;span class="na"&gt;o&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind&lt;/span&gt;
      &lt;span class="na"&gt;device&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/${USER}/data/wordpress&lt;/span&gt;

&lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db_root_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../secrets/db_root_password.txt&lt;/span&gt;
  &lt;span class="na"&gt;db_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../secrets/db_password.txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Analysis Questions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why does &lt;code&gt;wordpress&lt;/code&gt; depend on &lt;code&gt;mariadb&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;restart: always&lt;/code&gt; do?&lt;/li&gt;
&lt;li&gt;Why are volumes mapped to host directories?
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For testing we need to create data folders&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/user/data/mariadb
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/user/data/wordpress

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 13: The Makefile - Automation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Use a Makefile?
&lt;/h3&gt;

&lt;p&gt;A Makefile provides simple commands to manage your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Variables
&lt;/span&gt;&lt;span class="nv"&gt;COMPOSE_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; srcs/docker-compose.yml
&lt;span class="nv"&gt;DATA_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; /home/&lt;span class="p"&gt;$(&lt;/span&gt;USER&lt;span class="p"&gt;)&lt;/span&gt;/data

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;all build up down clean fclean re&lt;/span&gt;

&lt;span class="nl"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build up&lt;/span&gt;

&lt;span class="c"&gt;# Create data directories
&lt;/span&gt;&lt;span class="nl"&gt;$(DATA_DIR)/mariadb&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;DATA_DIR&lt;span class="p"&gt;)&lt;/span&gt;/mariadb

&lt;span class="nl"&gt;$(DATA_DIR)/wordpress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;DATA_DIR&lt;span class="p"&gt;)&lt;/span&gt;/wordpress

&lt;span class="c"&gt;# Build images
&lt;/span&gt;&lt;span class="nl"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$(DATA_DIR)/mariadb $(DATA_DIR)/wordpress&lt;/span&gt;
    docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;COMPOSE_FILE&lt;span class="p"&gt;)&lt;/span&gt; build

&lt;span class="c"&gt;# Start services
&lt;/span&gt;&lt;span class="nl"&gt;up&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;COMPOSE_FILE&lt;span class="p"&gt;)&lt;/span&gt; up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Stop services
&lt;/span&gt;&lt;span class="nl"&gt;down&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;COMPOSE_FILE&lt;span class="p"&gt;)&lt;/span&gt; down

&lt;span class="c"&gt;# Clean containers and images
&lt;/span&gt;&lt;span class="nl"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;COMPOSE_FILE&lt;span class="p"&gt;)&lt;/span&gt; down
    docker system prune &lt;span class="nt"&gt;-af&lt;/span&gt;

&lt;span class="c"&gt;# Full clean including volumes
&lt;/span&gt;&lt;span class="nl"&gt;fclean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;clean&lt;/span&gt;
    docker volume prune &lt;span class="nt"&gt;-f&lt;/span&gt;
    &lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;DATA_DIR&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Rebuild everything
&lt;/span&gt;&lt;span class="nl"&gt;re&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;fclean all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Understanding Make&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why do we create directories first?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 14: Testing and Debugging
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step-by-Step Testing
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build and start services:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check container status:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Debugging Exercise&lt;/strong&gt;: If a container is not running, how would you investigate?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check container logs&lt;/span&gt;
docker logs mariadb
docker logs wordpress
docker logs nginx

&lt;span class="c"&gt;# Access container shell&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mariadb bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test database connection:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mariadb mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Test WordPress:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Visit &lt;code&gt;https://login.42.fr&lt;/code&gt; (or your domain)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Troubleshooting Questions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What if you get "connection refused"?&lt;/li&gt;
&lt;li&gt;What if you see nginx but not WordPress?&lt;/li&gt;
&lt;li&gt;What if the database connection fails?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common Issues and Solutions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Issue&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Port conflict&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bind: address already in use&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop conflicting services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission denied&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mkdir: cannot create directory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check file permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network issues&lt;/td&gt;
&lt;td&gt;&lt;code&gt;could not connect to mariadb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verify network configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSL warnings&lt;/td&gt;
&lt;td&gt;Browser security warning&lt;/td&gt;
&lt;td&gt;Expected with self-signed certs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Part 15: Advanced Concepts and Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding PID 1
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🤔 Why is PID 1 special in containers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Unix systems, PID 1 is the init process that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages child processes&lt;/li&gt;
&lt;li&gt;Handles system signals&lt;/li&gt;
&lt;li&gt;Cleans up zombie processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Critical Thinking&lt;/strong&gt;: What happens if your container process doesn't handle SIGTERM?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Good - process runs as PID 1&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;

&lt;span class="c"&gt;# Bad - shell runs as PID 1&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; nginx -g "daemon off;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 16: Final Project Assembly
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Complete Directory Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inception/
├── Makefile
├── secrets/
│   ├── db_password.txt
│   └── db_root_password.txt
└── srcs/
    ├── docker-compose.yml
    ├── .env
    └── requirements/
        ├── mariadb/
        │   ├── Dockerfile
        │   ├── conf/
        │   │   └── 50-server.cnf
        │   └── tools/
        │       └── init_db.sh
        ├── nginx/
        │   ├── Dockerfile
        │   ├── conf/
        │   │   └── nginx.conf
        │   └── tools/
        │       └── generate_ssl.sh
        └── wordpress/
            ├── Dockerfile
            ├── conf/
            │   └── www.conf
            └── tools/
                └── setup_wordpress.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Checklist
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Infrastructure Requirements&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] NGINX with TLSv1.2 or TLSv1.3 only&lt;/li&gt;
&lt;li&gt;[ ] WordPress with php-fpm (no nginx)&lt;/li&gt;
&lt;li&gt;[ ] MariaDB (no nginx)&lt;/li&gt;
&lt;li&gt;[ ] Two volumes: database and WordPress files&lt;/li&gt;
&lt;li&gt;[ ] Docker network connecting containers&lt;/li&gt;
&lt;li&gt;[ ] Containers restart on crash&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Security Requirements&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] No passwords in Dockerfiles&lt;/li&gt;
&lt;li&gt;[ ] Environment variables used&lt;/li&gt;
&lt;li&gt;[ ] Secrets properly managed&lt;/li&gt;
&lt;li&gt;[ ] No prohibited commands (tail -f, sleep infinity, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Architecture Requirements&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Each service in dedicated container&lt;/li&gt;
&lt;li&gt;[ ] Custom Dockerfiles (no pulling ready-made images)&lt;/li&gt;
&lt;li&gt;[ ] NGINX as sole entry point on port 443&lt;/li&gt;
&lt;li&gt;[ ] Proper domain name configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Launch Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build everything&lt;/span&gt;
make all

&lt;span class="c"&gt;# Check status&lt;/span&gt;
docker ps

&lt;span class="c"&gt;# View logs&lt;/span&gt;
docker logs nginx
docker logs wordpress
docker logs mariadb

&lt;span class="c"&gt;# Test the website&lt;/span&gt;
curl &lt;span class="nt"&gt;-k&lt;/span&gt; https://login.42.fr

&lt;span class="c"&gt;# Stop everything&lt;/span&gt;
make down

&lt;span class="c"&gt;# Clean everything&lt;/span&gt;
make fclean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 17: Troubleshooting Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Common Scenarios
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: Container won't start&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the logs&lt;/span&gt;
docker logs &amp;lt;container_name&amp;gt;

&lt;span class="c"&gt;# Check the Dockerfile&lt;/span&gt;
&lt;span class="c"&gt;# Common issues: wrong base image, missing packages, permission issues&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Debug Challenge&lt;/strong&gt;: If MariaDB container exits immediately, what are the first three things you'd check?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: Cannot connect to database&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test network connectivity&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;wordpress ping mariadb

&lt;span class="c"&gt;# Check database is listening&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;mariadb netstat &lt;span class="nt"&gt;-tuln&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;3306

&lt;span class="c"&gt;# Test database connection&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;mariadb mysql &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SHOW DATABASES;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Scenario 3: NGINX returns 502 Bad Gateway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analysis&lt;/strong&gt;: What does 502 mean, and what would you check?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if WordPress container is running&lt;/span&gt;
docker ps | &lt;span class="nb"&gt;grep &lt;/span&gt;wordpress

&lt;span class="c"&gt;# Check PHP-FPM is listening&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;wordpress netstat &lt;span class="nt"&gt;-tuln&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;9000

&lt;span class="c"&gt;# Check NGINX configuration&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;nginx nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Investigation Questions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the issue CPU, memory, or I/O related?&lt;/li&gt;
&lt;li&gt;Are there any bottlenecks in the database queries?&lt;/li&gt;
&lt;li&gt;Is the network connectivity optimal?
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Monitor resource usage&lt;/span&gt;
docker stats

&lt;span class="c"&gt;# Check disk usage&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; /home/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/data/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Monitor network&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;nginx ss &lt;span class="nt"&gt;-tuln&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Docker containers basic commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="c"&gt;# To build&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;container_image_name&amp;gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# To run&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &amp;lt;container_image_name&amp;gt;

&lt;span class="c"&gt;# To run with name&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; my_app image

&lt;span class="c"&gt;# To check all docker containers&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# To stop containers and remove images&lt;/span&gt;
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;  docker rmi &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker images &lt;span class="nt"&gt;-aq&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# To run a compose file&lt;/span&gt;
docker compose up

&lt;span class="c"&gt;# Force rebuild in background &lt;/span&gt;
docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Down composer&lt;/span&gt;
docker compose down

&lt;span class="c"&gt;# To delete content from data volumes&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /home/user/data/wordpress/
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /home/user/data/mariadb/

&lt;span class="c"&gt;# To create again&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/user/data/wordpress/
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/user/data/mariadb/

&lt;span class="c"&gt;# To check db connection manually (from wp contaier)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; wordpress bash
apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; mariadb-client
mysql &lt;span class="nt"&gt;-h&lt;/span&gt; mariadb &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;

&lt;span class="c"&gt;# To acces mariadb user table from root (inside mariadb contaier)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mariadb mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;

&lt;span class="c"&gt;# And from wp_user (inside mariadb contaier)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mariadb mysql &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;

&lt;span class="c"&gt;# To check env values&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mariadb &lt;span class="nb"&gt;env
&lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; wordpress &lt;span class="nb"&gt;env&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker Documentation&lt;/strong&gt;: &lt;a href="https://docs.docker.com/" rel="noopener noreferrer"&gt;https://docs.docker.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Best Practices&lt;/strong&gt;: &lt;a href="https://docs.docker.com/develop/best-practices/" rel="noopener noreferrer"&gt;https://docs.docker.com/develop/best-practices/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Learning&lt;/strong&gt;: &lt;a href="https://kubernetes.io/docs/tutorials/" rel="noopener noreferrer"&gt;https://kubernetes.io/docs/tutorials/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Security&lt;/strong&gt;: &lt;a href="https://www.nist.gov/publications/application-container-security-guide" rel="noopener noreferrer"&gt;https://www.nist.gov/publications/application-container-security-guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>docker</category>
      <category>wordpress</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
