Tools: How I Deployed a Live Website Using Docker on Azure (And Let Cloud-Init Do the Heavy Lifting) - Guide

Tools: How I Deployed a Live Website Using Docker on Azure (And Let Cloud-Init Do the Heavy Lifting) - Guide

What I Built

Step 1: Provisioning the Azure VM

Step 2: The Cloud-Init Script (This Is the Magic Part)

Step 3: SSH Into the VM

Step 4: Clone the Static Website

Step 5: Write the Dockerfile

Step 6: Build the Image

Step 7: Run the Container

Step 8: Verify and Open in Browser

What I Learned

Full Project I created the VM through the Azure Portal with these settings: Under the Networking tab, I opened port 22 for SSH and port 80 for HTTP traffic by adding inbound rules to the Network Security Group. Before launching the VM, I pasted this script under the Advanced tab in the Custom Data field: This script runs automatically the moment the VM boots. Docker installs itself, starts itself, and adds my user to the docker group. I had not even SSH'd in yet. The log confirmed everything. Docker was installed by the startup script, not by me. That is infrastructure automation doing exactly what it is supposed to do. Once deployment completed, I grabbed my public IP from the Azure Portal and connected: Then I verified Docker was running: Simple. Just an index.html. That is all we need. Let me break this down: FROM nginx:alpine — we are using a lightweight version of Nginx as our base image. Alpine Linux is tiny, which keeps our container small and fast. RUN rm -rf /usr/share/nginx/html/* — wipes out the default Nginx welcome page so our site shows instead. COPY . /usr/share/nginx/html — copies everything in our current folder (including index.html) into the Nginx web root. EXPOSE 80 — tells Docker this container will accept traffic on port 80. Docker pulls nginx:alpine, runs each step in the Dockerfile, and produces an image called static-site:latest. The whole process takes about a minute. After building, I checked the image sizes: Our image is actually slightly smaller than the base because we replaced Nginx's default content with our own lighter files. Breaking down the flags: Then I opened http://4.234.163.212 in my browser. I cannot fully explain how good that felt. Three months into learning DevOps, and I just served a live website from inside a Docker container running on a cloud VM I spun up myself. Cloud-init is a game changer. The idea that a VM can configure itself on boot, without any human touching it, is what separates manual setups from real infrastructure automation. Containers are not complicated. A Dockerfile is just a recipe. Build the recipe, get an image. Run the image, get a container. That is it. nginx:alpine is perfect for static sites. Small, fast, and it just works. The --restart flag matters. In production, you want your containers to survive reboots. Always add --restart unless-stopped. GitHub: https://github.com/vivianokose/cloud-vm-docker-deploy See you in the next one. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

#cloud-config package_update: true package_upgrade: true packages: - apt-transport-https - ca-certificates - curl - gnupg - lsb-release runcmd: - apt-get update -y - apt-get install -y docker.io - systemctl enable docker - systemctl start docker - usermod -aG docker azureuser #cloud-config package_update: true package_upgrade: true packages: - apt-transport-https - ca-certificates - curl - gnupg - lsb-release runcmd: - apt-get update -y - apt-get install -y docker.io - systemctl enable docker - systemctl start docker - usermod -aG docker azureuser #cloud-config package_update: true package_upgrade: true packages: - apt-transport-https - ca-certificates - curl - gnupg - lsb-release runcmd: - apt-get update -y - apt-get install -y docker.io - systemctl enable docker - systemctl start docker - usermod -aG docker azureuser sudo cat /var/log/cloud-init-output.log | grep -i docker sudo cat /var/log/cloud-init-output.log | grep -i docker sudo cat /var/log/cloud-init-output.log | grep -i docker chmod 400 docker-vm_key.pem ssh -i docker-vm_key.pem [email protected] chmod 400 docker-vm_key.pem ssh -i docker-vm_key.pem [email protected] chmod 400 docker-vm_key.pem ssh -i docker-vm_key.pem [email protected] docker --version # Docker version 29.1.3 docker ps # Empty, no containers yet. But Docker is alive. docker --version # Docker version 29.1.3 docker ps # Empty, no containers yet. But Docker is alive. docker --version # Docker version 29.1.3 docker ps # Empty, no containers yet. But Docker is alive. git clone https://github.com/pravinmishraaws/Azure-Static-Website.git cd Azure-Static-Website ls # README.md index.html git clone https://github.com/pravinmishraaws/Azure-Static-Website.git cd Azure-Static-Website ls # README.md index.html git clone https://github.com/pravinmishraaws/Azure-Static-Website.git cd Azure-Static-Website ls # README.md index.html FROM nginx:alpine RUN rm -rf /usr/share/nginx/html/* COPY . /usr/share/nginx/html EXPOSE 80 FROM nginx:alpine RUN rm -rf /usr/share/nginx/html/* COPY . /usr/share/nginx/html EXPOSE 80 FROM nginx:alpine RUN rm -rf /usr/share/nginx/html/* COPY . /usr/share/nginx/html EXPOSE 80 docker build -t static-site:latest . docker build -t static-site:latest . docker build -t static-site:latest . docker run -d --name static-site \ -p 80:80 \ --restart unless-stopped \ static-site:latest docker run -d --name static-site \ -p 80:80 \ --restart unless-stopped \ static-site:latest docker run -d --name static-site \ -p 80:80 \ --restart unless-stopped \ static-site:latest CONTAINER ID IMAGE PORTS NAMES 83cb16a6cb44 static-site:latest 0.0.0.0:80->80/tcp static-site CONTAINER ID IMAGE PORTS NAMES 83cb16a6cb44 static-site:latest 0.0.0.0:80->80/tcp static-site CONTAINER ID IMAGE PORTS NAMES 83cb16a6cb44 static-site:latest 0.0.0.0:80->80/tcp static-site - 1 Linux VM on Azure (Ubuntu 24.04 LTS, Standard D2lds v6) - A cloud-init script that installed Docker automatically on first boot - A Dockerized static website served by Nginx - A live URL accessible from anywhere in the world - -d runs the container in the background - --name static-site gives it a friendly name - -p 80:80 maps port 80 on the VM to port 80 inside the container - --restart unless-stopped means it comes back automatically after a VM reboot