[
  {
    "path": "001-static/Dockerfile",
    "content": "FROM nginx:alpine\nCOPY . /usr/share/nginx/html\n"
  },
  {
    "path": "001-static/README.md",
    "content": "*Instruction that is used to build an image. Use the docker image build command to create a new image using the instructions contained in the Dockerfile. This example creates a new Docker image called name:tag. Be sure to perform this command from within the directory containing the app code and Dockerfile.*\n\n#### Build Image\n\n> <code> docker build -t &lt;image_name&gt;:&lt;tag&gt; &lt;source_of_Dockerfile&gt;</code>\n\n  `$ docker build -t static-web:latest .`\n\n#### Docker images list\n\n  `$ docker image ls`\n\n#### Run Container\n\n> <code> docker run -d --name &lt;container_name&gt; -p &lt;host_port&gt;:&lt;container_port&gt; &lt;image_name&gt;:&lt;tag&gt;</code>\n\n  `$ docker container run -d --name=web1 -p=8080:80 static-web:latest`\n\n#### For Test\n\n  ` $ curl http://localhost:8080`\n\n*Open a web browser and navigate to the DNS name or IP address of the host that you are running the container from and point it to port 8080. You will see the following web page.*\n\n> http://localhost:8080"
  },
  {
    "path": "001-static/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <title>My First Web App</title>\n</head>\n<body>\n    <h1>Hello World Docker</h1>\n</body>\n</html>\n"
  },
  {
    "path": "002-static/Dockerfile",
    "content": "FROM nginx:latest\nCOPY . /usr/share/nginx/html"
  },
  {
    "path": "002-static/index.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Hello world</title>\n    </head>\n    <body>\n        <div align='center'>\n            <h1>Hello World</h1>\n            <img src=images/hello.png>\n        </div>\n    </body>\n</html>"
  },
  {
    "path": "003-php/Dockerfile",
    "content": "FROM php:apache\nCOPY index.php /var/www/html\nEXPOSE 80\n"
  },
  {
    "path": "003-php/index.php",
    "content": "<?php\n  echo \"Hello World!\";\n  echo \"PHP is so easy!\";\n?>\n"
  },
  {
    "path": "004-python/Dockerfile",
    "content": "# Use an official Python runtime as a parent image\nFROM python:2.7-slim\nWORKDIR /app\nADD . /app\nRUN pip install --trusted-host pypi.python.org Flask\nENV NAME World\nENV BGNAME=purple\nEXPOSE  4000\nCMD [\"python\", \"app.py\"]\n"
  },
  {
    "path": "004-python/app.py",
    "content": "from flask import Flask\nimport os\nimport socket\n\napp = Flask(__name__)\n@app.route(\"/\")\n\ndef hello():\n    html = \"<body bgcolor='{bgname}'><h3>Hello {name}!</h3> <b>Hostname:</b> {hostname}<br/></body>\"\n    return html.format(name=os.getenv(\"NAME\", \"world\"), hostname=socket.gethostname(), bgname=os.getenv(\"BGNAME\", \"orange\"))\n\nif __name__ == \"__main__\":\n    app.run(host='0.0.0.0', port=4000)\n"
  },
  {
    "path": "004-python/readme.md",
    "content": "docker build -t mypyweb .\n\ndocker run --name webapp -p 8080:4000 mypyweb\n\ndocker run --name webapp -p 8080:4000 -e NAME=\"Dude\" -e BGNAME=\"blue\" mypyweb\n\n#### Share an image\ndocker login\ndocker tag mypyweb ameenalam/python-example:1.0\ndocker push uqutub/python-example:1.0\ndocker run -p 8080:4000 --name webapp -e NAME=\"Docker Hub\" ameenalam/python-example:1.0\n"
  },
  {
    "path": "005-ubuntu/Dockerfile",
    "content": "FROM ubuntu:16.04\n\nRUN apt-get update\nRUN apt-get upgrade -y\nRUN apt-get dist-upgrade -y\nRUN apt-get install curl htop git zip nano ncdu build-essential chrpath libssl-dev libxft-dev pkg-config glib2.0-dev libexpat1-dev gobject-introspection python-gi-dev apt-transport-https libgirepository1.0-dev libtiff5-dev libjpeg-turbo8-dev libgsf-1-dev fail2ban nginx -y\n\n# Install Node.js\nRUN curl -sL https://deb.nodesource.com/setup_8.x | bash\nRUN apt-get install --yes nodejs\nRUN node -v\nRUN npm -v\nRUN npm i -g nodemon\nRUN nodemon -v\n\n# Cleanup\nRUN apt-get update && apt-get upgrade -y && apt-get autoremove -y\n\n# Node App \nCOPY . /app\nWORKDIR /app\n\n# Install app dependencies\nRUN npm install\n\n# Binds to port 3000\nEXPOSE  3000\n\n#  Defines your runtime(define default command)\n# These commands unlike RUN (they are carried out in the construction of the container) are run when the container\nCMD [\"node\", \"app.js\"]\n"
  },
  {
    "path": "005-ubuntu/app.js",
    "content": "const express = require('express');\nconst app = express();\nconst port = 3000;\n\napp.get('/', (req, res) => res.send('Hello World! NodeJS'));\n\napp.listen(port, () => console.log(`Example app listening on port ${port}!`));\n"
  },
  {
    "path": "005-ubuntu/package.json",
    "content": "{\n  \"name\": \"demojsapp\",\n  \"version\": \"1.0.0\",\n  \"description\": \"demo nodejs app in ubuntu\",\n  \"main\": \"app.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/ameen-alam/CNC-Docker.git\"\n  },\n  \"author\": \"Ameen Alam\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"express\": \"^4.17.0\"\n  }\n}\n"
  },
  {
    "path": "005-ubuntu/readme.md",
    "content": "```sh\n$ docker run -it ameen /bin/bash\n```\n"
  },
  {
    "path": "006-nodejs/.dockerignore",
    "content": "node_modules\nnpm-debug.log\n"
  },
  {
    "path": "006-nodejs/Dockerfile",
    "content": "FROM node:8\n\n# Create app directory\nWORKDIR /usr/src/app\n\n# Install app dependencies\n# A wildcard is used to ensure both package.json AND package-lock.json are copied\n# where available (npm@5+)\nCOPY package*.json ./\n\nRUN npm install\n# If you are building your code for production\n# RUN npm ci --only=production\n\n# Bundle app source\nCOPY . .\n\nEXPOSE 8080\nCMD [ \"npm\", \"start\" ]\n"
  },
  {
    "path": "006-nodejs/README.md",
    "content": "*Instruction that is used to build an image. Use the docker image build command to create a new image using the instructions contained in the Dockerfile. This example creates a new Docker image called name:tag. Be sure to perform this command from within the directory containing the app code and Dockerfile.*\n\n#### Build Image\n\n> <code> docker build -t &lt;image_name&gt;:&lt;tag&gt; &lt;source_of_Dockerfile&gt;</code>\n\n  `$ docker build -t node-app:latest .`\n\n#### Docker images list\n\n  `$ docker image ls`\n\n#### Run Container\n\n> <code> docker run -d --name &lt;container_name&gt; -p &lt;host_port&gt;:&lt;container_port&gt; &lt;image_name&gt;:&lt;tag&gt;</code>\n\n  `$ docker container run -d --name=web1 -p=9000:8080 node-app:latest`\n\n#### For Test\n\n  ` $ curl http://localhost:9000`\n\n*Open a web browser and navigate to the DNS name or IP address of the host that you are running the container from and point it to port 9000. You will see the following web page.*\n\n> http://localhost:9000"
  },
  {
    "path": "006-nodejs/package.json",
    "content": "{\n  \"name\": \"docker_web_app\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Node.js on Docker\",\n  \"author\": \"Ameen ALam <ameenalam202@gmail.com>\",\n  \"main\": \"server.js\",\n  \"scripts\": {\n    \"start\": \"node server.js\"\n  },\n  \"dependencies\": {\n    \"express\": \"^4.16.1\"\n  }\n}\n"
  },
  {
    "path": "006-nodejs/server.js",
    "content": "'use strict';\n\nconst express = require('express');\n\n// Constants\nconst PORT = 8080;\nconst HOST = '0.0.0.0';\n\n// App\nconst app = express();\napp.get('/', (req, res) => {\n  res.send('Hello world\\n');\n});\n\napp.listen(PORT, HOST);\nconsole.log(`Running on http://${HOST}:${PORT}`);\n"
  },
  {
    "path": "007-volumeMount/readme.md",
    "content": "```sh\n$ docker run -v /full/path/to/html/directory:/usr/share/nginx/html:ro -p 8080:80 -d nginx:alpine\n```\n\nWhen we execute this command line, we see Docker download the Nginx image and then start the container.\nWe used four command line options to run this container:\n\t-\t-v /full/path/to/html/directory:/usr/share/nginx/html:ro maps the directory holding our web page to the required location in the image. The ro field instructs Docker to mount it in read-only mode. It’s best to pass Docker the full paths when specifying host directories.\n\t-\t-p 8080:80 maps network service port 80 in the container to 8080 on our host system.\n\t-\t-d detaches the container from our command line session. we don’t want to interact with this container.\n\t-\tnginx is the name of the image.\n"
  },
  {
    "path": "008-mysql/readme.md",
    "content": "```sh\n$ docker run -p 3306:3306 --name mysqlserver -e MYSQL_ROOT_PASSWORD=root -d -v /home/mysql-data:/var/lib/mysql mysql\n```\n#### Listing existing mounts\n```sh\n$ docker inspect mysqlserver\n```\n"
  },
  {
    "path": "009-mssql/readme.md",
    "content": "```sh\n$ docker run -e \"ACCEPT_EULA=Y\" -e \"SA_PASSWORD=mypassword\" -e \"MSSQL_PID=Express\" -p 1433:1433 -v /path/your/myfolder:/var/opt/mssql --name HIMS-WEB -d microsoft/mssql-server-linux:latest\n```"
  },
  {
    "path": "010-mongoDB/readme.md",
    "content": "### The docker run -d -p 27017:27107 -v ~/data:/data/db mongo does 3 main things:\n\n```sh\n$ docker run -d -p 27017:27107 -v ~/data:/data/db mongo\n```\n\n- -d tells docker to run the container as a daemon, which is the mode that'll you want to use for server containers.\n- -p 27017:27107 maps the port 27017 of the container to the port 27017 of the host. The syntax is -p HOST_PORT:CONTAINER_PORT.\n- -v ~/data:/data/db maps the /data/db directory of the container to the ~/data directory on the host. This is called a data volume, the principal mechanism to import and export data with your docker container."
  },
  {
    "path": "Cheat Sheet/README.md",
    "content": "\n### Cheat Sheet\n\n| Command | Description |\n| ------ | ------ |\n| docker pull &lt;image-name&gt;:&lt;tag&gt; | Pulls the image from the docker hub |\n| docker build -t &lt;image-name&gt;:&lt;tag&gt;  . | Builds the image from the Dockerfile with the mentioned name and tag |\n| docker image ls | Shows the list of the images present on your system. short-hand 'docker images' |\n| docker container ls | Displays the only running containers. short-hand 'docker ps'  |\n| docker container ls -a | Displays all the containers present on your system. short-hand 'docker ps  -a' |\n| docker inspect &lt;image name&gt;:&lt;tag&gt; | Shows the detailed information about the image in JSON format. |\n| docker history &lt;image name&gt;:&lt;tag&gt; | Used to inspect the layers of the image. |\n| docker tag &lt;source-image&gt;:&lt;tag&gt; &lt;username/new-image-name&gt;:&lt;tag&gt; | Create a tag of the new image that refers to source image.  |\n|docker push user/&lt;image-name&gt;:&lt;tag&gt; | Push an image to a registry |\n| docker image rm &lt;image name&gt;:&lt;tag&gt; | Remove the image. short-hand 'docker rmi &lt;image name&gt;:&lt;tag&gt;' |\n| docker run --name &lt;container_name&gt; -p &lt;host_port : container_port&gt; -d &lt;image_name&gt; | Create the container with the specified name and assign the specified port from the image. |\n| docker run --name &lt;container-name&gt; -it -p &lt;host_port : container_port&gt; &lt;image-name&gt;:&lt;tag&gt; sh | To run a container from an image in an interactive mode. Press Ctrl + pq it will detach terminal and leave container running in background. |\n| docker exec -it &lt;container_name&gt; sh | To go in the running container shell. Write exit to detach the terminal |\n| docker stop container_name | It will stop the running container. |\n| docker start container_name | Start the stopped container |\n| docker rm container_name | Remove the container. |\n| docker logs container_name | fetch the logs of the container |\n| docker volume create my-vol | Create your Volume for Persistent Data |\n| docker volumes ls | List down the volumes |\n| docker volume inspect my-vol | inspect the volumes |\n| docker volumes rm my-vol | remove volume |\n| docker run -d --name mycont -v my-vol:/usr/share/nginx/html nginx:latest | start container with -v flag (volume mount) |\n| docker run -d --name devtest --mount source=my-vol,target=/usr/share/nginx/html nginx:latest | start container with --mount flag |\n| docker run -d -it --name devtest -v \"$(pwd)\"/myfolder:/app nginx:latest | start container with bind mounts and -v flag |\n| docker run -d -it --name devtest --mount type=bind,source=\"$(pwd)\"/myfolder,target=/app nginx:latest | start container with bind mounts and -mount flag |\n\n"
  },
  {
    "path": "Install-Docker/README.md",
    "content": "# Install Docker with Linux\n\n*There are two ways of installing it. The official docker way is a bit more recent.*\n\n##### 1st way..\n\n### A) Official Ubuntu Repositories\n\n`$ sudo apt-get install docker.io`\n\n> and after verify the installation\n\n`$ docker -v`\n\n`$ sudo docker run hello-world`\n\n------------------------\n\n##### 2nd way\n\n### (B) Official Docker Way\n\n**(1) Set up the docker repository and Install Docker CE**\n\n> Updating the apt package index\n\n`$ sudo apt-get update`\n\n> Installing packages to allow apt to use a repository over HTTPS:\n\n`$ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common`\n\n> Adding Docker’s official key GPG\n\n`$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -`\n\n> Verifying that you now have the key with fingerprint:\n\n`$ sudo apt-key fingerprint 0EBFCD88`\n\n> sub-command below returns the name of your Ubuntu distribution\n\n`$ sudo add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\"`\n\n> Updating the apt package index\n\n`$ sudo apt-get update`\n\n> Installing the latest version of Docker CE and containerd\n\n`$ sudo apt-get -y install docker-ce docker-ce-cli containerd.io`\n\n> and after verify the installation\n\n`$ docker -v`\n\n`$ sudo docker run hello-world`\n\n\nDocker install by source\n\nhttps://docs.docker.com/engine/install/binaries/\nhttps://download.docker.com/linux/static/stable/x86_64/\nhttps://docs.docker.com/compose/install/\n\n###### Thankyou.\n"
  },
  {
    "path": "Install-Docker/installDocker.sh",
    "content": "#!/bin/sh\necho --------------------------------------------\necho Installing Script, Docker created by: Uqutub\necho --------------------------------------------\necho Updating the apt package index:\necho --------------------------------------------\nsudo apt-get update\necho -------------------------------------------- \necho Installing packages to allow apt to use a repository over HTTPS:\necho --------------------------------------------\nsudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common\necho --------------------------------------------\necho Adding Docker’s official key GPG\necho --------------------------------------------\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -\necho --------------------------------------------\necho Verifying that you now have the key with fingerprint:\necho --------------------------------------------\nsudo apt-key fingerprint 0EBFCD88\necho --------------------------------------------\necho sub-command below returns the name of your Ubuntu distribution:\necho --------------------------------------------\nsudo add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\"\necho --------------------------------------------\necho Updating the apt package index:\necho --------------------------------------------\nsudo apt-get update\necho --------------------------------------------\necho Installing the latest version of Docker CE and containerd,\necho --------------------------------------------\nsudo apt-get -y install docker-ce docker-ce-cli containerd.io"
  },
  {
    "path": "README.md",
    "content": "# Deploy Application using Docker with Nginx\n\n[Docker Deep Dive Book PDF](/Docker%20Deep%20Dive.pdf)\n\n#### Docker Tutorial\n\n\n[Docker for beginners tutorial](https://www.youtube.com/playlist?list=PLRnB5kp_ilgbsl8hJwhQXmSos8Ua9Tc_d)\n\n\n[https://github.com/Ameen-Alam/CNC-Docker.git](https://github.com/Ameen-Alam/CNC-Docker.git) Clone the sample app from GitHub.\n\n`$ git clone https://github.com/Ameen-Alam/CNC-Docker.git`\n\n**The clone operation creates a new directory called CNC-Docker . Change directory into CNC-Docker and list its contents.**\n\n`$ cd CNC-Docker`\n\n> Available Multiple Image Directories (001-static --to-- 010-mongoDB)\n\n`$ cd 001-static`\n\n__two way to run a container__\n<ul>\n <li>The Ops perspective</li>\n <li>The Dev perspective</li>\n</ul>\n\n--------------------------------\n\n### Start The Ops Perspective\n\n*If you are working from a freshly installed Docker host it will have no images and will look like the output above.\nGetting images onto your Docker host is called “pulling”. If you are following along with Linux, pull the ameenalam/nodeapp:latest.*\n\n#### To pull an image\n\n> <code> docker pull &lt;image_name&gt;:&lt;tag&gt;</code>\n\n  `$ docker image pull ameenalam/nodeapp:latest`\n\n#### Docker images list\n\n  `$ docker image ls`\n\n#### Run Container\n\n> <code> docker run -d --name &lt;container_name&gt; -p &lt;host_port&gt;:&lt;container_port&gt; &lt;image_name&gt;:&lt;tag&gt;</code>\n\n  `$ docker container run -d --name=web2 -p=7000:8080 ameenalam/nodeapp:latest`\n\n#### For Test\n\n  ` $ curl http://localhost:7000`\n\n*Open a web browser and navigate to the DNS name or IP address of the host that you are running the container from and point it to port 7000. You will see the following web page.*\n\nhttp://localhost:7000/\n\n-----------------------------------\n\n# Push images to Docker Cloud\n\n*Docker Cloud uses Docker Hub as its native registry for storing both public and private repositories. Once you push your images to Docker Hub, they are available in Docker Cloud.*\n\n__In order to get you started, let us get you a Docker ID.__\n\nhttps://hub.docker.com/\n\n#### Docker login \n\n> Log in to Docker Cloud using the docker login command.\n\n`$ docker login`\n\n#### Tag Image\n\n> Tag your image using docker tag.\n\n`$ docker tag image_name username/image_name`\n\n#### Push your image to Docker Hub\n\n> Push your image to Docker Hub using docker push (making the same replacements as in the previous step).\n\n `$ docker push username/image_name`\n\n\n*Check that the image you just pushed appears in Docker Cloud.\nGo to Docker Cloud and navigate to the Repositories tab and confirm that your image appears in this list.*\n\nFacebook Profile: https://www.facebook.com/SheikhAmeenAlam\n\n###### Thankyou.\n"
  },
  {
    "path": "_config.yml",
    "content": "theme: jekyll-theme-slate"
  },
  {
    "path": "hands-on-command-journey.md",
    "content": "# Hands-on Command Journey\n\n## 1) Core Docker commands (the “must know” list)\n\n### Help + version\n\n```bash\ndocker --help\ndocker version\ndocker info\n```\n\n### Images\n\n```bash\ndocker images\ndocker pull python:3.12-slim\ndocker rmi <image_id>\ndocker image prune\n```\n\n### Containers (run/stop/logs/exec)\n\n```bash\ndocker ps\ndocker ps -a\n\ndocker run hello-world\ndocker run --name mybox -it ubuntu:24.04 bash\n\ndocker stop mybox\ndocker start mybox\ndocker restart mybox\ndocker rm mybox\n\ndocker logs <container>\ndocker logs -f <container>\n\ndocker exec -it <container> bash\n```\n\n### Ports + env + naming\n\n```bash\ndocker run --name api -p 8000:8000 -e ENV=dev python:3.12-slim\n```\n\n### Volumes (persistency)\n\n```bash\ndocker volume ls\ndocker volume create appdata\ndocker volume inspect appdata\ndocker run -v appdata:/data -it ubuntu:24.04 bash\ndocker volume rm appdata\ndocker volume prune\n```\n\n### Bind mounts (dev workflow)\n\n```bash\ndocker run -it --rm -v \"%cd%\":/app -w /app python:3.12-slim bash\n# (Linux/Mac: -v \"$(pwd)\":/app)\n```\n\n### Networks (service-to-service)\n\n```bash\ndocker network ls\ndocker network create appnet\ndocker network inspect appnet\ndocker run -d --name redis --network appnet redis:7\ndocker run -it --rm --network appnet redis:7 redis-cli -h redis ping\n```\n\n### Cleanup (important for students’ laptops 😄)\n\n```bash\ndocker system df\ndocker system prune\ndocker system prune -a --volumes\n```\n\n---\n\n## 2) Dockerfile example: FastAPI + uv (production-ready pattern)\n\n> Assumption: you run FastAPI with **uvicorn** (common). If you truly meant “uv” the Python package manager, tell me and I’ll adjust.\n\n### `Dockerfile` (multi-stage, small-ish, prod)\n\n```dockerfile\n# ---- base stage ----\nFROM python:3.12-slim AS base\nWORKDIR /app\nENV PYTHONDONTWRITEBYTECODE=1 \\\n    PYTHONUNBUFFERED=1\n\n# System deps (adjust if you need build tools)\nRUN apt-get update && apt-get install -y --no-install-recommends \\\n    curl \\\n  && rm -rf /var/lib/apt/lists/*\n\n# ---- deps stage ----\nFROM base AS deps\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\n\n# ---- runtime stage ----\nFROM base AS runtime\nCOPY --from=deps /usr/local /usr/local\nCOPY . .\n\nEXPOSE 8000\nCMD [\"uvicorn\", \"app.main:app\", \"--host=0.0.0.0\", \"--port=8000\"]\n```\n\n**Build + run**\n\n```bash\ndocker build -t fastapi-app:prod .\ndocker run --rm -p 8000:8000 fastapi-app:prod\n```\n\n---\n\n## 3) Dev image vs Prod image (what to teach)\n\n### Development container (hot reload + bind mount)\n\nRun (no Dockerfile required, but you can still use one):\n\n```bash\ndocker run --rm -it \\\n  -p 8000:8000 \\\n  -v \"%cd%\":/app -w /app \\\n  python:3.12-slim \\\n  bash -lc \"pip install -r requirements.txt && uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload\"\n```\n\n**Teaching point:** dev = bind mount + reload + bigger image acceptable.\n\n### Production container (immutable + no reload)\n\n* Copy code into image\n* No bind mounts\n* Use pinned deps\n* Add healthchecks (optional)\n* Run as non-root (advanced best practice)\n\n---\n\n## 4) Next.js Dockerfile (production) + dev command\n\n### Prod `Dockerfile` (Next.js)\n\n```dockerfile\nFROM node:20-alpine AS deps\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\n\nFROM node:20-alpine AS build\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\nRUN npm run build\n\nFROM node:20-alpine AS runtime\nWORKDIR /app\nENV NODE_ENV=production\nCOPY --from=build /app ./\nEXPOSE 3000\nCMD [\"npm\", \"run\", \"start\"]\n```\n\nDev run (bind mount):\n\n```bash\ndocker run --rm -it -p 3000:3000 -v \"%cd%\":/app -w /app node:20-alpine sh -lc \"npm i && npm run dev\"\n```\n\n---\n\n## 5) Docker Compose starter (FastAPI + Next.js + Postgres + Redis)\n\n`docker-compose.yml`\n\n```yaml\nservices:\n  db:\n    image: postgres:16\n    environment:\n      POSTGRES_USER: app\n      POSTGRES_PASSWORD: app\n      POSTGRES_DB: appdb\n    volumes:\n      - pgdata:/var/lib/postgresql/data\n    ports:\n      - \"5432:5432\"\n\n  redis:\n    image: redis:7\n    ports:\n      - \"6379:6379\"\n\n  api:\n    build: ./api\n    environment:\n      DATABASE_URL: postgresql://app:app@db:5432/appdb\n      REDIS_URL: redis://redis:6379/0\n    ports:\n      - \"8000:8000\"\n    depends_on:\n      - db\n      - redis\n\n  web:\n    build: ./web\n    ports:\n      - \"3000:3000\"\n    depends_on:\n      - api\n\nvolumes:\n  pgdata:\n```\n\nCompose commands:\n\n```bash\ndocker compose up -d --build\ndocker compose ps\ndocker compose logs -f api\ndocker compose down\ndocker compose down -v\n```\n\n---\n\n## 6) AI Agents SDK + background tasks + persistency (how to explain in Docker terms)\n\n* **Background tasks** (FastAPI BackgroundTasks / Celery / RQ):\n  run as **separate container** (a “worker”) or same container (not recommended for scale).\n* **Persistency**: use **volumes** for DB, vector DB, file storage, etc.\n* **Networking**: containers talk by **service name** in compose (`db`, `redis`, `api`).\n\nIf you want a clean “agents + worker” pattern:\n\n* `api` container (HTTP)\n* `worker` container (process jobs)\n* `redis` (queue)\n* `db` (state)\n"
  }
]