I Deployed Docker App on Azure — Part 1: Local + ACI (Working)
Stack: Node.js 20 + Express + PostgreSQL 16 + Docker + Azure Container Instances
Repo old: https://github.com/krisking7/lexszy-note
New repo:https://github.com/Shack793/Docker-to-Cloud.git
My Docker Hub: devshadd/notes-app:v1 → https://hub.docker.com/r/devshadd/notes-app
Azure: Subscription 1, eastus, SUFFIX=dev12
Live ACI: http://notes-app-dev12.eastus.azurecontainer.io:3000 (IP 48.194.78.11:3000)(Pulled Down To avoid billing-refer to screenshot)
Core lesson: same image everywhere, config via env, vars DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME, DB_SSL.
STEP 1 — Prepare Tools
Ran:
git --version # 2.52.0
docker --version # 29.8.1
docker compose version # v5.5.1
az --version # 2.90.0
Challenge: docker info failed — failed to connect to docker API at npipe:////./pipe/dockerDesktopLinuxEngine.
Fix: Docker Desktop installed at %LOCALAPPDATA%\Programs\DockerDesktop\Docker Desktop.exe (not C:\Program Files\Docker).
Started it, waited 5s, docker info → Server=29.8.1 OS=Docker Desktop.
STEP 2 — Get Code + Run Locally first
cd C:\laragon2\www\Cloud-Docker
git clone https://github.com/krisking7/lexszy-note.git
cd lexszy-note
dir # Dockerfile, docker-compose.yaml, package.json, server.js, public/
30-sec tour:
server.js — Express API, creates notes table on startup, /health → {"status":"ok"}
public/index.html — frontend
Dockerfile — FROM node:20-alpine > WORKDIR /app > COPY package*.json > RUN npm install > COPY . . > EXPOSE 3000 >
CMD ["node","server.js"]
- docker-compose.yaml — app reaches DB at hostname db, volume db_data
Ran:
docker compose up -d --build
docker compose ps # app + db Up
docker compose logs app
Challenge 1: First build timed out after 120s pulling postgres:16-alpine + node:20-alpine.
Fix: Re-ran with 300s timeout, resumed and finished: added 82 packages in 26s.
Challenge 2: Logs showed:
Notes app running on port 3000
Database initialization failed: ECONNREFUSED 172.18.0.2:5432
depends_on only waits for container start, not DB ready.
Fix (per guide):
docker compose restart app
Start-Sleep 8
docker compose logs app
→ Notes app running on port 3000
→ Database initialized (notes table ready)
Test:
curl http://localhost:3000/health # {"status":"ok"}
Invoke-RestMethod http://localhost:3000/api/notes -Method Post -Body '{"title":"Test1"}'...
→ id:1 Test1 created, GET returns row
Challenge 3: curl.exe -X POST -d '{"title":...}' failed in PowerShell with Unterminated string in JSON.
Fix: Used PowerShell native:
$body = @{title="Test1";content="hello from step2"} | ConvertTo-Json
Invoke-RestMethod -Uri http://localhost:3000/api/notes -Method Post -Body $body -ContentType "application/json"
STEP 3 — Publish to Docker Hub
My username: devshadd (social login).
Changed docker-compose.yaml:6:
before:
image: krisking2024/notes-app:v1
after:
image: devshadd/notes-app:v1
docker tag krisking2024/notes-app:v1 devshadd/notes-app:v1
docker image ls # both → be7d37659533 212MB
docker push devshadd/notes-app:v1
Challenge: First push timed out: failed commit ... net/http: timeout awaiting response headers after some layers Pushed.
Fix: Re-ran docker push — resumable:
44136fa355b3: Already exists
4feea04c1543: Pushed
v1: digest: sha256:be7d376595330de84a093765019acda4ec777df4064570499210f5c6818c442e
Verified at https://hub.docker.com/r/devshadd/notes-app → tag v1, 48.9MB, docker pull devshadd/notes-app:v1.

Social-login note: if docker login asks password, create Hub PAT: Hub → Account Settings → Personal Access Tokens → use token as password.
STEP 4 — Prepare Azure
Already logged in:
az account show --output table # Subscription 1 Enabled
az provider register --namespace Microsoft.ContainerInstance
az provider register --namespace Microsoft.DBforPostgreSQL
az provider register --namespace Microsoft.Web
first → Registering, after 1min → Registered
Vars (PowerShell):
$DOCKER_USER="devshadd"
$SUFFIX="dev12"
$LOCATION="eastus"
$DB_PASS="*** lab-only, redacted for post ***"
STEP 5 — Option A: ACI
az group create --name notes-app-rg --location eastus
Created aci-notes.yaml:
image: devshadd/notes-app:v1
command: ["/bin/sh", "-c", "sleep 20 && node server.js"]
DB_HOST: localhost # NOT db — ACI group shares localhost network
dnsNameLabel: notes-app-dev12
secureValue: "***" # hides password
az container create --resource-group notes-app-rg --file aci-notes.yaml
Challenge: First create → MultipleErrorsOccurred Conflict RegistryErrorResponse index.docker.io. Please retry later.
Fix: Waited 30s, retried — succeeded with full JSON ID returned.
Verify:
az container show --resource-group notes-app-rg --name notes-app-group --query "{provisioning:provisioningState, fqdn:ipAddress.fqdn, ip:ipAddress.ip}" --output table
Succeeded notes-app-dev12.eastus.azurecontainer.io 48.194.78.11
az container logs --resource-group notes-app-rg --name notes-app-group --container-name app
Notes app running on port 3000
Database initialized (notes table ready)
curl http://notes-app-dev12.eastus.azurecontainer.io:3000/health # {"status":"ok"}
Challenge: Browser ERR_NAME_NOT_RESOLVED for notes-app-dev12.eastus.
Fix: URL was truncated. Must use full + http + :3000:
Lesson for post:
ACI = fastest, closest to compose, but ephemeral — delete/recreate loses data (no volume). No HTTPS.
Cleanup (run after screenshots):
az group delete --name notes-app-rg --yes --no-wait
Biggest lessons: depends_on ≠ DB ready (needed restart/sleep), same image runs everywhere via env vars.
ACI was fast but ephemeral.
#Azure #Docker #DevOps





Top comments (0)