[{"data":1,"prerenderedAt":2385},["ShallowReactive",2],{"\u002Fdeploy\u002Fflask-plus-docker-compose-production-setup":3},{"id":4,"title":5,"body":6,"description":2375,"extension":2376,"meta":2377,"navigation":67,"path":2381,"seo":2382,"stem":2383,"__hash__":2384},"content\u002Fdeploy\u002Fflask-plus-docker-compose-production-setup.md","Flask + Docker Compose Production Setup",{"type":7,"value":8,"toc":2333},"minimark",[9,13,17,22,25,488,491,499,503,514,517,536,540,545,548,554,561,567,603,607,613,673,679,688,692,698,725,731,738,741,923,926,943,947,958,1045,1058,1062,1065,1068,1094,1097,1139,1143,1146,1149,1166,1169,1198,1201,1230,1233,1237,1240,1257,1260,1274,1287,1291,1294,1307,1310,1324,1326,1331,1343,1347,1350,1387,1390,1393,1409,1413,1416,1451,1454,1474,1477,1481,1484,1516,1518,1524,1528,1531,1534,1670,1673,1677,1680,1741,1744,1748,1857,1861,1864,1868,1880,1882,1896,1900,1918,1920,1939,1943,1960,1962,1982,1986,2016,2019,2053,2057,2089,2092,2096,2126,2130,2162,2165,2178,2182,2251,2255,2279,2283,2287,2290,2294,2297,2301,2304,2308,2311,2319,2322,2326,2329],[10,11,5],"h1",{"id":12},"flask-docker-compose-production-setup",[14,15,16],"p",{},"If you're trying to run Flask in production with Docker Compose, this guide shows you how to set up the app, Gunicorn, and Nginx step-by-step. The goal is a repeatable deployment that starts cleanly, serves traffic correctly, and is easy to debug.",[18,19,21],"h2",{"id":20},"quick-fix-quick-setup","Quick Fix \u002F Quick Setup",[14,23,24],{},"Use this minimal production-style stack first. It verifies container startup, Gunicorn binding, Docker networking, and Nginx reverse proxy behavior before you add your full application, database, TLS, or persistent storage.",[26,27,32],"pre",{"className":28,"code":29,"language":30,"meta":31,"style":31},"language-bash shiki shiki-themes github-light github-dark","mkdir -p flask-compose-prod && cd flask-compose-prod\n\ncat > app.py \u003C\u003C'PY'\nfrom flask import Flask\napp = Flask(__name__)\n\n@app.route('\u002F')\ndef index():\n    return 'Flask Compose production OK\\n'\nPY\n\ncat > requirements.txt \u003C\u003C'TXT'\nflask==3.0.3\ngunicorn==22.0.0\nTXT\n\ncat > Dockerfile \u003C\u003C'DOCKER'\nFROM python:3.12-slim\nWORKDIR \u002Fapp\nENV PYTHONDONTWRITEBYTECODE=1\nENV PYTHONUNBUFFERED=1\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\nCOPY . .\nCMD [\"gunicorn\", \"-w\", \"3\", \"-b\", \"0.0.0.0:8000\", \"app:app\"]\nDOCKER\n\ncat > docker-compose.yml \u003C\u003C'YAML'\nservices:\n  web:\n    build: .\n    restart: unless-stopped\n    expose:\n      - \"8000\"\n  nginx:\n    image: nginx:stable-alpine\n    restart: unless-stopped\n    ports:\n      - \"80:80\"\n    volumes:\n      - .\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf:ro\n    depends_on:\n      - web\nYAML\n\ncat > nginx.conf \u003C\u003C'NGINX'\nserver {\n    listen 80;\n    server_name _;\n\n    location \u002F {\n        proxy_pass http:\u002F\u002Fweb:8000;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\nNGINX\n\ndocker compose up -d --build\ncurl -I http:\u002F\u002Flocalhost\n","bash","",[33,34,35,62,69,88,94,100,105,111,117,123,129,134,149,155,161,167,172,187,193,199,205,211,217,223,229,235,241,246,261,267,273,279,285,291,297,303,309,314,320,326,332,338,344,350,356,361,376,382,388,394,399,405,411,417,423,429,435,441,447,453,458,476],"code",{"__ignoreMap":31},[36,37,40,44,48,52,56,59],"span",{"class":38,"line":39},"line",1,[36,41,43],{"class":42},"sScJk","mkdir",[36,45,47],{"class":46},"sj4cs"," -p",[36,49,51],{"class":50},"sZZnC"," flask-compose-prod",[36,53,55],{"class":54},"sVt8B"," && ",[36,57,58],{"class":46},"cd",[36,60,61],{"class":50}," flask-compose-prod\n",[36,63,65],{"class":38,"line":64},2,[36,66,68],{"emptyLinePlaceholder":67},true,"\n",[36,70,72,75,79,82,85],{"class":38,"line":71},3,[36,73,74],{"class":42},"cat",[36,76,78],{"class":77},"szBVR"," >",[36,80,81],{"class":50}," app.py",[36,83,84],{"class":77}," \u003C\u003C",[36,86,87],{"class":50},"'PY'\n",[36,89,91],{"class":38,"line":90},4,[36,92,93],{"class":50},"from flask import Flask\n",[36,95,97],{"class":38,"line":96},5,[36,98,99],{"class":50},"app = Flask(__name__)\n",[36,101,103],{"class":38,"line":102},6,[36,104,68],{"emptyLinePlaceholder":67},[36,106,108],{"class":38,"line":107},7,[36,109,110],{"class":50},"@app.route('\u002F')\n",[36,112,114],{"class":38,"line":113},8,[36,115,116],{"class":50},"def index():\n",[36,118,120],{"class":38,"line":119},9,[36,121,122],{"class":50},"    return 'Flask Compose production OK\\n'\n",[36,124,126],{"class":38,"line":125},10,[36,127,128],{"class":50},"PY\n",[36,130,132],{"class":38,"line":131},11,[36,133,68],{"emptyLinePlaceholder":67},[36,135,137,139,141,144,146],{"class":38,"line":136},12,[36,138,74],{"class":42},[36,140,78],{"class":77},[36,142,143],{"class":50}," requirements.txt",[36,145,84],{"class":77},[36,147,148],{"class":50},"'TXT'\n",[36,150,152],{"class":38,"line":151},13,[36,153,154],{"class":50},"flask==3.0.3\n",[36,156,158],{"class":38,"line":157},14,[36,159,160],{"class":50},"gunicorn==22.0.0\n",[36,162,164],{"class":38,"line":163},15,[36,165,166],{"class":50},"TXT\n",[36,168,170],{"class":38,"line":169},16,[36,171,68],{"emptyLinePlaceholder":67},[36,173,175,177,179,182,184],{"class":38,"line":174},17,[36,176,74],{"class":42},[36,178,78],{"class":77},[36,180,181],{"class":50}," Dockerfile",[36,183,84],{"class":77},[36,185,186],{"class":50},"'DOCKER'\n",[36,188,190],{"class":38,"line":189},18,[36,191,192],{"class":50},"FROM python:3.12-slim\n",[36,194,196],{"class":38,"line":195},19,[36,197,198],{"class":50},"WORKDIR \u002Fapp\n",[36,200,202],{"class":38,"line":201},20,[36,203,204],{"class":50},"ENV PYTHONDONTWRITEBYTECODE=1\n",[36,206,208],{"class":38,"line":207},21,[36,209,210],{"class":50},"ENV PYTHONUNBUFFERED=1\n",[36,212,214],{"class":38,"line":213},22,[36,215,216],{"class":50},"COPY requirements.txt .\n",[36,218,220],{"class":38,"line":219},23,[36,221,222],{"class":50},"RUN pip install --no-cache-dir -r requirements.txt\n",[36,224,226],{"class":38,"line":225},24,[36,227,228],{"class":50},"COPY . .\n",[36,230,232],{"class":38,"line":231},25,[36,233,234],{"class":50},"CMD [\"gunicorn\", \"-w\", \"3\", \"-b\", \"0.0.0.0:8000\", \"app:app\"]\n",[36,236,238],{"class":38,"line":237},26,[36,239,240],{"class":50},"DOCKER\n",[36,242,244],{"class":38,"line":243},27,[36,245,68],{"emptyLinePlaceholder":67},[36,247,249,251,253,256,258],{"class":38,"line":248},28,[36,250,74],{"class":42},[36,252,78],{"class":77},[36,254,255],{"class":50}," docker-compose.yml",[36,257,84],{"class":77},[36,259,260],{"class":50},"'YAML'\n",[36,262,264],{"class":38,"line":263},29,[36,265,266],{"class":50},"services:\n",[36,268,270],{"class":38,"line":269},30,[36,271,272],{"class":50},"  web:\n",[36,274,276],{"class":38,"line":275},31,[36,277,278],{"class":50},"    build: .\n",[36,280,282],{"class":38,"line":281},32,[36,283,284],{"class":50},"    restart: unless-stopped\n",[36,286,288],{"class":38,"line":287},33,[36,289,290],{"class":50},"    expose:\n",[36,292,294],{"class":38,"line":293},34,[36,295,296],{"class":50},"      - \"8000\"\n",[36,298,300],{"class":38,"line":299},35,[36,301,302],{"class":50},"  nginx:\n",[36,304,306],{"class":38,"line":305},36,[36,307,308],{"class":50},"    image: nginx:stable-alpine\n",[36,310,312],{"class":38,"line":311},37,[36,313,284],{"class":50},[36,315,317],{"class":38,"line":316},38,[36,318,319],{"class":50},"    ports:\n",[36,321,323],{"class":38,"line":322},39,[36,324,325],{"class":50},"      - \"80:80\"\n",[36,327,329],{"class":38,"line":328},40,[36,330,331],{"class":50},"    volumes:\n",[36,333,335],{"class":38,"line":334},41,[36,336,337],{"class":50},"      - .\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf:ro\n",[36,339,341],{"class":38,"line":340},42,[36,342,343],{"class":50},"    depends_on:\n",[36,345,347],{"class":38,"line":346},43,[36,348,349],{"class":50},"      - web\n",[36,351,353],{"class":38,"line":352},44,[36,354,355],{"class":50},"YAML\n",[36,357,359],{"class":38,"line":358},45,[36,360,68],{"emptyLinePlaceholder":67},[36,362,364,366,368,371,373],{"class":38,"line":363},46,[36,365,74],{"class":42},[36,367,78],{"class":77},[36,369,370],{"class":50}," nginx.conf",[36,372,84],{"class":77},[36,374,375],{"class":50},"'NGINX'\n",[36,377,379],{"class":38,"line":378},47,[36,380,381],{"class":50},"server {\n",[36,383,385],{"class":38,"line":384},48,[36,386,387],{"class":50},"    listen 80;\n",[36,389,391],{"class":38,"line":390},49,[36,392,393],{"class":50},"    server_name _;\n",[36,395,397],{"class":38,"line":396},50,[36,398,68],{"emptyLinePlaceholder":67},[36,400,402],{"class":38,"line":401},51,[36,403,404],{"class":50},"    location \u002F {\n",[36,406,408],{"class":38,"line":407},52,[36,409,410],{"class":50},"        proxy_pass http:\u002F\u002Fweb:8000;\n",[36,412,414],{"class":38,"line":413},53,[36,415,416],{"class":50},"        proxy_set_header Host $host;\n",[36,418,420],{"class":38,"line":419},54,[36,421,422],{"class":50},"        proxy_set_header X-Real-IP $remote_addr;\n",[36,424,426],{"class":38,"line":425},55,[36,427,428],{"class":50},"        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n",[36,430,432],{"class":38,"line":431},56,[36,433,434],{"class":50},"        proxy_set_header X-Forwarded-Proto $scheme;\n",[36,436,438],{"class":38,"line":437},57,[36,439,440],{"class":50},"    }\n",[36,442,444],{"class":38,"line":443},58,[36,445,446],{"class":50},"}\n",[36,448,450],{"class":38,"line":449},59,[36,451,452],{"class":50},"NGINX\n",[36,454,456],{"class":38,"line":455},60,[36,457,68],{"emptyLinePlaceholder":67},[36,459,461,464,467,470,473],{"class":38,"line":460},61,[36,462,463],{"class":42},"docker",[36,465,466],{"class":50}," compose",[36,468,469],{"class":50}," up",[36,471,472],{"class":46}," -d",[36,474,475],{"class":46}," --build\n",[36,477,479,482,485],{"class":38,"line":478},62,[36,480,481],{"class":42},"curl",[36,483,484],{"class":46}," -I",[36,486,487],{"class":50}," http:\u002F\u002Flocalhost\n",[14,489,490],{},"Expected result:",[26,492,497],{"className":493,"code":495,"language":496,"meta":31},[494],"language-text","HTTP\u002F1.1 200 OK\n","text",[33,498,495],{"__ignoreMap":31},[18,500,502],{"id":501},"whats-happening","What’s Happening",[14,504,505,506,509,510,513],{},"Docker Compose defines multiple services on a shared internal network. Gunicorn serves the Flask WSGI application inside the ",[33,507,508],{},"web"," container, and Nginx handles public HTTP traffic and proxies requests to ",[33,511,512],{},"web:8000",".",[14,515,516],{},"Most production failures come from one of these conditions:",[518,519,520,524,527,530,533],"ul",{},[521,522,523],"li",{},"Wrong Gunicorn entrypoint",[521,525,526],{},"Gunicorn binding to the wrong interface",[521,528,529],{},"Nginx proxying to the wrong service name or port",[521,531,532],{},"Missing environment variables",[521,534,535],{},"Missing persistent volumes for uploads or durable files",[18,537,539],{"id":538},"step-by-step-guide","Step-by-Step Guide",[541,542,544],"h3",{"id":543},"_1-create-a-production-project-layout","1. Create a production project layout",[14,546,547],{},"Use a structure that separates app code, proxy config, and environment files.",[26,549,552],{"className":550,"code":551,"language":496,"meta":31},[494],"flask-compose-prod\u002F\n├── app.py\n├── requirements.txt\n├── Dockerfile\n├── docker-compose.yml\n├── .env\n└── nginx.conf\n",[33,553,551],{"__ignoreMap":31},[14,555,556,557,560],{},"If your Flask app uses a factory pattern, add a ",[33,558,559],{},"wsgi.py"," file instead of pointing Gunicorn directly at your package internals.",[14,562,563,564,566],{},"Example ",[33,565,559],{},":",[26,568,572],{"className":569,"code":570,"language":571,"meta":31,"style":31},"language-python shiki shiki-themes github-light github-dark","from myapp import create_app\n\napp = create_app()\n","python",[33,573,574,588,592],{"__ignoreMap":31},[36,575,576,579,582,585],{"class":38,"line":39},[36,577,578],{"class":77},"from",[36,580,581],{"class":54}," myapp ",[36,583,584],{"class":77},"import",[36,586,587],{"class":54}," create_app\n",[36,589,590],{"class":38,"line":64},[36,591,68],{"emptyLinePlaceholder":67},[36,593,594,597,600],{"class":38,"line":71},[36,595,596],{"class":54},"app ",[36,598,599],{"class":77},"=",[36,601,602],{"class":54}," create_app()\n",[541,604,606],{"id":605},"_2-build-a-production-dockerfile-for-flask-gunicorn","2. Build a production Dockerfile for Flask + Gunicorn",[14,608,609,610,513],{},"Use a slim Python image, install only required dependencies, and bind Gunicorn to ",[33,611,612],{},"0.0.0.0",[26,614,618],{"className":615,"code":616,"language":617,"meta":31,"style":31},"language-dockerfile shiki shiki-themes github-light github-dark","FROM python:3.12-slim\n\nWORKDIR \u002Fapp\n\nENV PYTHONDONTWRITEBYTECODE=1\nENV PYTHONUNBUFFERED=1\n\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\n\nCOPY . .\n\nCMD [\"gunicorn\", \"--workers\", \"3\", \"--bind\", \"0.0.0.0:8000\", \"app:app\"]\n","dockerfile",[33,619,620,624,628,632,636,640,644,648,652,656,660,664,668],{"__ignoreMap":31},[36,621,622],{"class":38,"line":39},[36,623,192],{},[36,625,626],{"class":38,"line":64},[36,627,68],{"emptyLinePlaceholder":67},[36,629,630],{"class":38,"line":71},[36,631,198],{},[36,633,634],{"class":38,"line":90},[36,635,68],{"emptyLinePlaceholder":67},[36,637,638],{"class":38,"line":96},[36,639,204],{},[36,641,642],{"class":38,"line":102},[36,643,210],{},[36,645,646],{"class":38,"line":107},[36,647,68],{"emptyLinePlaceholder":67},[36,649,650],{"class":38,"line":113},[36,651,216],{},[36,653,654],{"class":38,"line":119},[36,655,222],{},[36,657,658],{"class":38,"line":125},[36,659,68],{"emptyLinePlaceholder":67},[36,661,662],{"class":38,"line":131},[36,663,228],{},[36,665,666],{"class":38,"line":136},[36,667,68],{"emptyLinePlaceholder":67},[36,669,670],{"class":38,"line":151},[36,671,672],{},"CMD [\"gunicorn\", \"--workers\", \"3\", \"--bind\", \"0.0.0.0:8000\", \"app:app\"]\n",[14,674,675,676,678],{},"If your entrypoint is ",[33,677,559],{},", change the final argument:",[26,680,682],{"className":615,"code":681,"language":617,"meta":31,"style":31},"CMD [\"gunicorn\", \"--workers\", \"3\", \"--bind\", \"0.0.0.0:8000\", \"wsgi:app\"]\n",[33,683,684],{"__ignoreMap":31},[36,685,686],{"class":38,"line":39},[36,687,681],{},[541,689,691],{"id":690},"_3-define-environment-variables","3. Define environment variables",[14,693,694,695,513],{},"Add application settings to ",[33,696,697],{},".env",[26,699,703],{"className":700,"code":701,"language":702,"meta":31,"style":31},"language-env shiki shiki-themes github-light github-dark","SECRET_KEY=change-this\nFLASK_ENV=production\nDATABASE_URL=postgresql:\u002F\u002Fuser:password@db:5432\u002Fappdb\nREDIS_URL=redis:\u002F\u002Fredis:6379\u002F0\n","env",[33,704,705,710,715,720],{"__ignoreMap":31},[36,706,707],{"class":38,"line":39},[36,708,709],{},"SECRET_KEY=change-this\n",[36,711,712],{"class":38,"line":64},[36,713,714],{},"FLASK_ENV=production\n",[36,716,717],{"class":38,"line":71},[36,718,719],{},"DATABASE_URL=postgresql:\u002F\u002Fuser:password@db:5432\u002Fappdb\n",[36,721,722],{"class":38,"line":90},[36,723,724],{},"REDIS_URL=redis:\u002F\u002Fredis:6379\u002F0\n",[14,726,727,728,730],{},"Use ",[33,729,697],{}," for Compose variable injection and service environment loading where appropriate. Do not hardcode secrets in the image.",[541,732,734,735],{"id":733},"_4-configure-docker-composeyml","4. Configure ",[33,736,737],{},"docker-compose.yml",[14,739,740],{},"Expose the app internally and publish only Nginx to the public network.",[26,742,746],{"className":743,"code":744,"language":745,"meta":31,"style":31},"language-yaml shiki shiki-themes github-light github-dark","services:\n  web:\n    build: .\n    restart: unless-stopped\n    env_file:\n      - .env\n    expose:\n      - \"8000\"\n    volumes:\n      - uploads:\u002Fapp\u002Fuploads\n\n  nginx:\n    image: nginx:stable-alpine\n    restart: unless-stopped\n    depends_on:\n      - web\n    ports:\n      - \"80:80\"\n    volumes:\n      - .\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf:ro\n      - uploads:\u002Fapp\u002Fuploads:ro\n\nvolumes:\n  uploads:\n","yaml",[33,747,748,757,764,775,785,792,800,807,814,821,828,832,839,849,857,864,871,878,885,891,898,905,909,916],{"__ignoreMap":31},[36,749,750,754],{"class":38,"line":39},[36,751,753],{"class":752},"s9eBZ","services",[36,755,756],{"class":54},":\n",[36,758,759,762],{"class":38,"line":64},[36,760,761],{"class":752},"  web",[36,763,756],{"class":54},[36,765,766,769,772],{"class":38,"line":71},[36,767,768],{"class":752},"    build",[36,770,771],{"class":54},": ",[36,773,774],{"class":46},".\n",[36,776,777,780,782],{"class":38,"line":90},[36,778,779],{"class":752},"    restart",[36,781,771],{"class":54},[36,783,784],{"class":50},"unless-stopped\n",[36,786,787,790],{"class":38,"line":96},[36,788,789],{"class":752},"    env_file",[36,791,756],{"class":54},[36,793,794,797],{"class":38,"line":102},[36,795,796],{"class":54},"      - ",[36,798,799],{"class":50},".env\n",[36,801,802,805],{"class":38,"line":107},[36,803,804],{"class":752},"    expose",[36,806,756],{"class":54},[36,808,809,811],{"class":38,"line":113},[36,810,796],{"class":54},[36,812,813],{"class":50},"\"8000\"\n",[36,815,816,819],{"class":38,"line":119},[36,817,818],{"class":752},"    volumes",[36,820,756],{"class":54},[36,822,823,825],{"class":38,"line":125},[36,824,796],{"class":54},[36,826,827],{"class":50},"uploads:\u002Fapp\u002Fuploads\n",[36,829,830],{"class":38,"line":131},[36,831,68],{"emptyLinePlaceholder":67},[36,833,834,837],{"class":38,"line":136},[36,835,836],{"class":752},"  nginx",[36,838,756],{"class":54},[36,840,841,844,846],{"class":38,"line":151},[36,842,843],{"class":752},"    image",[36,845,771],{"class":54},[36,847,848],{"class":50},"nginx:stable-alpine\n",[36,850,851,853,855],{"class":38,"line":157},[36,852,779],{"class":752},[36,854,771],{"class":54},[36,856,784],{"class":50},[36,858,859,862],{"class":38,"line":163},[36,860,861],{"class":752},"    depends_on",[36,863,756],{"class":54},[36,865,866,868],{"class":38,"line":169},[36,867,796],{"class":54},[36,869,870],{"class":50},"web\n",[36,872,873,876],{"class":38,"line":174},[36,874,875],{"class":752},"    ports",[36,877,756],{"class":54},[36,879,880,882],{"class":38,"line":189},[36,881,796],{"class":54},[36,883,884],{"class":50},"\"80:80\"\n",[36,886,887,889],{"class":38,"line":195},[36,888,818],{"class":752},[36,890,756],{"class":54},[36,892,893,895],{"class":38,"line":201},[36,894,796],{"class":54},[36,896,897],{"class":50},".\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf:ro\n",[36,899,900,902],{"class":38,"line":207},[36,901,796],{"class":54},[36,903,904],{"class":50},"uploads:\u002Fapp\u002Fuploads:ro\n",[36,906,907],{"class":38,"line":213},[36,908,68],{"emptyLinePlaceholder":67},[36,910,911,914],{"class":38,"line":219},[36,912,913],{"class":752},"volumes",[36,915,756],{"class":54},[36,917,918,921],{"class":38,"line":225},[36,919,920],{"class":752},"  uploads",[36,922,756],{"class":54},[14,924,925],{},"Key points:",[518,927,928,934,940],{},[521,929,727,930,933],{},[33,931,932],{},"expose"," for the Flask app container",[521,935,727,936,939],{},[33,937,938],{},"ports"," only on Nginx",[521,941,942],{},"Mount volumes for any files that must survive rebuilds",[541,944,946],{"id":945},"_5-configure-nginx-as-the-reverse-proxy","5. Configure Nginx as the reverse proxy",[14,948,949,950,953,954,957],{},"Create ",[33,951,952],{},"nginx.conf"," and point ",[33,955,956],{},"proxy_pass"," to the Compose service name.",[26,959,963],{"className":960,"code":961,"language":962,"meta":31,"style":31},"language-nginx shiki shiki-themes github-light github-dark","server {\n    listen 80;\n    server_name _;\n\n    client_max_body_size 20M;\n\n    location \u002F {\n        proxy_pass http:\u002F\u002Fweb:8000;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_redirect off;\n    }\n\n    location \u002Fuploads\u002F {\n        alias \u002Fapp\u002Fuploads\u002F;\n    }\n}\n","nginx",[33,964,965,969,973,977,981,986,990,994,998,1002,1006,1010,1014,1019,1023,1027,1032,1037,1041],{"__ignoreMap":31},[36,966,967],{"class":38,"line":39},[36,968,381],{},[36,970,971],{"class":38,"line":64},[36,972,387],{},[36,974,975],{"class":38,"line":71},[36,976,393],{},[36,978,979],{"class":38,"line":90},[36,980,68],{"emptyLinePlaceholder":67},[36,982,983],{"class":38,"line":96},[36,984,985],{},"    client_max_body_size 20M;\n",[36,987,988],{"class":38,"line":102},[36,989,68],{"emptyLinePlaceholder":67},[36,991,992],{"class":38,"line":107},[36,993,404],{},[36,995,996],{"class":38,"line":113},[36,997,410],{},[36,999,1000],{"class":38,"line":119},[36,1001,416],{},[36,1003,1004],{"class":38,"line":125},[36,1005,422],{},[36,1007,1008],{"class":38,"line":131},[36,1009,428],{},[36,1011,1012],{"class":38,"line":136},[36,1013,434],{},[36,1015,1016],{"class":38,"line":151},[36,1017,1018],{},"        proxy_redirect off;\n",[36,1020,1021],{"class":38,"line":157},[36,1022,440],{},[36,1024,1025],{"class":38,"line":163},[36,1026,68],{"emptyLinePlaceholder":67},[36,1028,1029],{"class":38,"line":169},[36,1030,1031],{},"    location \u002Fuploads\u002F {\n",[36,1033,1034],{"class":38,"line":174},[36,1035,1036],{},"        alias \u002Fapp\u002Fuploads\u002F;\n",[36,1038,1039],{"class":38,"line":189},[36,1040,440],{},[36,1042,1043],{"class":38,"line":195},[36,1044,446],{},[14,1046,1047,1048,1051,1052,1057],{},"If you also serve static files through Nginx, add a dedicated ",[33,1049,1050],{},"location \u002Fstatic\u002F"," block and mount the static directory into the Nginx container. See ",[1053,1054,1056],"a",{"href":1055},"\u002Ffix-issues\u002Fflask-static-files-not-loading-in-production","Flask Static Files Not Loading in Production"," if CSS, JS, or image assets fail after deployment.",[541,1059,1061],{"id":1060},"_6-confirm-the-flaskgunicorn-entrypoint","6. Confirm the Flask\u002FGunicorn entrypoint",[14,1063,1064],{},"This is one of the most common failure points.",[14,1066,1067],{},"Use the correct Gunicorn module path:",[518,1069,1070,1083,1091],{},[521,1071,1072,1075,1076,1079,1080],{},[33,1073,1074],{},"app:app"," if your Flask object is ",[33,1077,1078],{},"app"," in ",[33,1081,1082],{},"app.py",[521,1084,1085,1088,1089],{},[33,1086,1087],{},"wsgi:app"," if your WSGI entrypoint is ",[33,1090,559],{},[521,1092,1093],{},"A dedicated WSGI file for app factories",[14,1095,1096],{},"Test import correctness inside the container later with:",[26,1098,1100],{"className":28,"code":1099,"language":30,"meta":31,"style":31},"docker exec -it $(docker compose ps -q web) python -c \"import app; print('import-ok')\"\n",[33,1101,1102],{"__ignoreMap":31},[36,1103,1104,1106,1109,1112,1115,1117,1119,1122,1125,1128,1131,1133,1136],{"class":38,"line":39},[36,1105,463],{"class":42},[36,1107,1108],{"class":50}," exec",[36,1110,1111],{"class":46}," -it",[36,1113,1114],{"class":54}," $(",[36,1116,463],{"class":42},[36,1118,466],{"class":50},[36,1120,1121],{"class":50}," ps",[36,1123,1124],{"class":46}," -q",[36,1126,1127],{"class":50}," web",[36,1129,1130],{"class":54},") ",[36,1132,571],{"class":50},[36,1134,1135],{"class":46}," -c",[36,1137,1138],{"class":50}," \"import app; print('import-ok')\"\n",[541,1140,1142],{"id":1141},"_7-add-persistent-storage-for-uploads-or-durable-files","7. Add persistent storage for uploads or durable files",[14,1144,1145],{},"Do not rely on the container filesystem for long-term storage. Container replacement removes in-container data.",[14,1147,1148],{},"Example named volume in Compose:",[26,1150,1152],{"className":743,"code":1151,"language":745,"meta":31,"style":31},"volumes:\n  uploads:\n",[33,1153,1154,1160],{"__ignoreMap":31},[36,1155,1156,1158],{"class":38,"line":39},[36,1157,913],{"class":752},[36,1159,756],{"class":54},[36,1161,1162,1164],{"class":38,"line":64},[36,1163,920],{"class":752},[36,1165,756],{"class":54},[14,1167,1168],{},"Mounted into the app container:",[26,1170,1172],{"className":743,"code":1171,"language":745,"meta":31,"style":31},"services:\n  web:\n    volumes:\n      - uploads:\u002Fapp\u002Fuploads\n",[33,1173,1174,1180,1186,1192],{"__ignoreMap":31},[36,1175,1176,1178],{"class":38,"line":39},[36,1177,753],{"class":752},[36,1179,756],{"class":54},[36,1181,1182,1184],{"class":38,"line":64},[36,1183,761],{"class":752},[36,1185,756],{"class":54},[36,1187,1188,1190],{"class":38,"line":71},[36,1189,818],{"class":752},[36,1191,756],{"class":54},[36,1193,1194,1196],{"class":38,"line":90},[36,1195,796],{"class":54},[36,1197,827],{"class":50},[14,1199,1200],{},"Mounted into Nginx for direct file serving:",[26,1202,1204],{"className":743,"code":1203,"language":745,"meta":31,"style":31},"services:\n  nginx:\n    volumes:\n      - uploads:\u002Fapp\u002Fuploads:ro\n",[33,1205,1206,1212,1218,1224],{"__ignoreMap":31},[36,1207,1208,1210],{"class":38,"line":39},[36,1209,753],{"class":752},[36,1211,756],{"class":54},[36,1213,1214,1216],{"class":38,"line":64},[36,1215,836],{"class":752},[36,1217,756],{"class":54},[36,1219,1220,1222],{"class":38,"line":71},[36,1221,818],{"class":752},[36,1223,756],{"class":54},[36,1225,1226,1228],{"class":38,"line":90},[36,1227,796],{"class":54},[36,1229,904],{"class":50},[14,1231,1232],{},"For larger deployments, use object storage instead of container volumes for user-generated files.",[541,1234,1236],{"id":1235},"_8-build-and-start-the-stack","8. Build and start the stack",[14,1238,1239],{},"Build images and launch services in detached mode.",[26,1241,1243],{"className":28,"code":1242,"language":30,"meta":31,"style":31},"docker compose up -d --build\n",[33,1244,1245],{"__ignoreMap":31},[36,1246,1247,1249,1251,1253,1255],{"class":38,"line":39},[36,1248,463],{"class":42},[36,1250,466],{"class":50},[36,1252,469],{"class":50},[36,1254,472],{"class":46},[36,1256,475],{"class":46},[14,1258,1259],{},"Check service state:",[26,1261,1263],{"className":28,"code":1262,"language":30,"meta":31,"style":31},"docker compose ps\n",[33,1264,1265],{"__ignoreMap":31},[36,1266,1267,1269,1271],{"class":38,"line":39},[36,1268,463],{"class":42},[36,1270,466],{"class":50},[36,1272,1273],{"class":50}," ps\n",[14,1275,1276,1277,1279,1280,1282,1283,1286],{},"Expected output: both ",[33,1278,508],{}," and ",[33,1281,962],{}," are ",[33,1284,1285],{},"running"," and not restarting.",[541,1288,1290],{"id":1289},"_9-validate-http-routing-through-nginx","9. Validate HTTP routing through Nginx",[14,1292,1293],{},"Test from the host:",[26,1295,1297],{"className":28,"code":1296,"language":30,"meta":31,"style":31},"curl -I http:\u002F\u002Flocalhost\n",[33,1298,1299],{"__ignoreMap":31},[36,1300,1301,1303,1305],{"class":38,"line":39},[36,1302,481],{"class":42},[36,1304,484],{"class":46},[36,1306,487],{"class":50},[14,1308,1309],{},"Or from a remote machine:",[26,1311,1313],{"className":28,"code":1312,"language":30,"meta":31,"style":31},"curl -I http:\u002F\u002FSERVER_IP\n",[33,1314,1315],{"__ignoreMap":31},[36,1316,1317,1319,1321],{"class":38,"line":39},[36,1318,481],{"class":42},[36,1320,484],{"class":46},[36,1322,1323],{"class":50}," http:\u002F\u002FSERVER_IP\n",[14,1325,490],{},[26,1327,1329],{"className":1328,"code":495,"language":496,"meta":31},[494],[33,1330,495],{"__ignoreMap":31},[14,1332,1333,1334,1337,1338,1342],{},"If you get ",[33,1335,1336],{},"502 Bad Gateway",", inspect the proxy and app logs immediately. Also see ",[1053,1339,1341],{"href":1340},"\u002Ffix-issues\u002Fflask-docker-container-not-starting-fix-guide","Flask Docker Container Not Starting (Fix Guide)"," for container boot failures.",[541,1344,1346],{"id":1345},"_10-validate-service-to-service-networking","10. Validate service-to-service networking",[14,1348,1349],{},"From inside the Nginx container, verify the upstream app responds on the Docker network.",[26,1351,1353],{"className":28,"code":1352,"language":30,"meta":31,"style":31},"docker exec -it $(docker compose ps -q nginx) wget -qO- http:\u002F\u002Fweb:8000\n",[33,1354,1355],{"__ignoreMap":31},[36,1356,1357,1359,1361,1363,1365,1367,1369,1371,1373,1376,1378,1381,1384],{"class":38,"line":39},[36,1358,463],{"class":42},[36,1360,1108],{"class":50},[36,1362,1111],{"class":46},[36,1364,1114],{"class":54},[36,1366,463],{"class":42},[36,1368,466],{"class":50},[36,1370,1121],{"class":50},[36,1372,1124],{"class":46},[36,1374,1375],{"class":50}," nginx",[36,1377,1130],{"class":54},[36,1379,1380],{"class":50},"wget",[36,1382,1383],{"class":46}," -qO-",[36,1385,1386],{"class":50}," http:\u002F\u002Fweb:8000\n",[14,1388,1389],{},"Expected output should include the Flask response body.",[14,1391,1392],{},"If this fails:",[518,1394,1395,1401,1406],{},[521,1396,1397,1398],{},"Gunicorn may not be listening on ",[33,1399,1400],{},"0.0.0.0:8000",[521,1402,1403,1405],{},[33,1404,956],{}," may reference the wrong service",[521,1407,1408],{},"The app container may be crashing",[541,1410,1412],{"id":1411},"_11-validate-environment-variables-inside-the-app-container","11. Validate environment variables inside the app container",[14,1414,1415],{},"Check that required values exist at runtime.",[26,1417,1419],{"className":28,"code":1418,"language":30,"meta":31,"style":31},"docker exec -it $(docker compose ps -q web) env | sort\n",[33,1420,1421],{"__ignoreMap":31},[36,1422,1423,1425,1427,1429,1431,1433,1435,1437,1439,1441,1443,1445,1448],{"class":38,"line":39},[36,1424,463],{"class":42},[36,1426,1108],{"class":50},[36,1428,1111],{"class":46},[36,1430,1114],{"class":54},[36,1432,463],{"class":42},[36,1434,466],{"class":50},[36,1436,1121],{"class":50},[36,1438,1124],{"class":46},[36,1440,1127],{"class":50},[36,1442,1130],{"class":54},[36,1444,702],{"class":50},[36,1446,1447],{"class":77}," |",[36,1449,1450],{"class":42}," sort\n",[14,1452,1453],{},"Look for:",[518,1455,1456,1461,1466,1471],{},[521,1457,1458],{},[33,1459,1460],{},"SECRET_KEY",[521,1462,1463],{},[33,1464,1465],{},"DATABASE_URL",[521,1467,1468],{},[33,1469,1470],{},"REDIS_URL",[521,1472,1473],{},"any external API credentials your app requires",[14,1475,1476],{},"Missing environment variables are a common reason deployments work locally but fail on the server.",[541,1478,1480],{"id":1479},"_12-validate-nginx-configuration-syntax","12. Validate Nginx configuration syntax",[14,1482,1483],{},"Before troubleshooting application code, confirm the proxy config is valid.",[26,1485,1487],{"className":28,"code":1486,"language":30,"meta":31,"style":31},"docker exec -it $(docker compose ps -q nginx) nginx -t\n",[33,1488,1489],{"__ignoreMap":31},[36,1490,1491,1493,1495,1497,1499,1501,1503,1505,1507,1509,1511,1513],{"class":38,"line":39},[36,1492,463],{"class":42},[36,1494,1108],{"class":50},[36,1496,1111],{"class":46},[36,1498,1114],{"class":54},[36,1500,463],{"class":42},[36,1502,466],{"class":50},[36,1504,1121],{"class":50},[36,1506,1124],{"class":46},[36,1508,1375],{"class":50},[36,1510,1130],{"class":54},[36,1512,962],{"class":50},[36,1514,1515],{"class":46}," -t\n",[14,1517,490],{},[26,1519,1522],{"className":1520,"code":1521,"language":496,"meta":31},[494],"nginx: the configuration file \u002Fetc\u002Fnginx\u002Fnginx.conf syntax is ok\nnginx: configuration file \u002Fetc\u002Fnginx\u002Fnginx.conf test is successful\n",[33,1523,1521],{"__ignoreMap":31},[541,1525,1527],{"id":1526},"_13-add-optional-database-or-redis-services-only-when-needed","13. Add optional database or Redis services only when needed",[14,1529,1530],{},"If you need a local PostgreSQL or Redis service, add them explicitly.",[14,1532,1533],{},"Example:",[26,1535,1537],{"className":743,"code":1536,"language":745,"meta":31,"style":31},"services:\n  db:\n    image: postgres:16-alpine\n    restart: unless-stopped\n    environment:\n      POSTGRES_DB: appdb\n      POSTGRES_USER: appuser\n      POSTGRES_PASSWORD: strongpassword\n    volumes:\n      - postgres_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n\n  redis:\n    image: redis:7-alpine\n    restart: unless-stopped\n\nvolumes:\n  uploads:\n  postgres_data:\n",[33,1538,1539,1545,1552,1561,1569,1576,1586,1596,1606,1612,1619,1623,1630,1639,1647,1651,1657,1663],{"__ignoreMap":31},[36,1540,1541,1543],{"class":38,"line":39},[36,1542,753],{"class":752},[36,1544,756],{"class":54},[36,1546,1547,1550],{"class":38,"line":64},[36,1548,1549],{"class":752},"  db",[36,1551,756],{"class":54},[36,1553,1554,1556,1558],{"class":38,"line":71},[36,1555,843],{"class":752},[36,1557,771],{"class":54},[36,1559,1560],{"class":50},"postgres:16-alpine\n",[36,1562,1563,1565,1567],{"class":38,"line":90},[36,1564,779],{"class":752},[36,1566,771],{"class":54},[36,1568,784],{"class":50},[36,1570,1571,1574],{"class":38,"line":96},[36,1572,1573],{"class":752},"    environment",[36,1575,756],{"class":54},[36,1577,1578,1581,1583],{"class":38,"line":102},[36,1579,1580],{"class":752},"      POSTGRES_DB",[36,1582,771],{"class":54},[36,1584,1585],{"class":50},"appdb\n",[36,1587,1588,1591,1593],{"class":38,"line":107},[36,1589,1590],{"class":752},"      POSTGRES_USER",[36,1592,771],{"class":54},[36,1594,1595],{"class":50},"appuser\n",[36,1597,1598,1601,1603],{"class":38,"line":113},[36,1599,1600],{"class":752},"      POSTGRES_PASSWORD",[36,1602,771],{"class":54},[36,1604,1605],{"class":50},"strongpassword\n",[36,1607,1608,1610],{"class":38,"line":119},[36,1609,818],{"class":752},[36,1611,756],{"class":54},[36,1613,1614,1616],{"class":38,"line":125},[36,1615,796],{"class":54},[36,1617,1618],{"class":50},"postgres_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n",[36,1620,1621],{"class":38,"line":131},[36,1622,68],{"emptyLinePlaceholder":67},[36,1624,1625,1628],{"class":38,"line":136},[36,1626,1627],{"class":752},"  redis",[36,1629,756],{"class":54},[36,1631,1632,1634,1636],{"class":38,"line":151},[36,1633,843],{"class":752},[36,1635,771],{"class":54},[36,1637,1638],{"class":50},"redis:7-alpine\n",[36,1640,1641,1643,1645],{"class":38,"line":157},[36,1642,779],{"class":752},[36,1644,771],{"class":54},[36,1646,784],{"class":50},[36,1648,1649],{"class":38,"line":163},[36,1650,68],{"emptyLinePlaceholder":67},[36,1652,1653,1655],{"class":38,"line":169},[36,1654,913],{"class":752},[36,1656,756],{"class":54},[36,1658,1659,1661],{"class":38,"line":174},[36,1660,920],{"class":752},[36,1662,756],{"class":54},[36,1664,1665,1668],{"class":38,"line":189},[36,1666,1667],{"class":752},"  postgres_data",[36,1669,756],{"class":54},[14,1671,1672],{},"If the app starts before the database is ready, implement retry logic or a wait strategy in your app startup path.",[541,1674,1676],{"id":1675},"_14-rebuild-and-roll-out-changes-safely","14. Rebuild and roll out changes safely",[14,1678,1679],{},"For updates:",[26,1681,1683],{"className":28,"code":1682,"language":30,"meta":31,"style":31},"docker compose up -d --build\ndocker compose ps\ndocker compose logs --tail=50 web\ndocker compose logs --tail=50 nginx\ncurl -I http:\u002F\u002Flocalhost\n",[33,1684,1685,1697,1705,1720,1733],{"__ignoreMap":31},[36,1686,1687,1689,1691,1693,1695],{"class":38,"line":39},[36,1688,463],{"class":42},[36,1690,466],{"class":50},[36,1692,469],{"class":50},[36,1694,472],{"class":46},[36,1696,475],{"class":46},[36,1698,1699,1701,1703],{"class":38,"line":64},[36,1700,463],{"class":42},[36,1702,466],{"class":50},[36,1704,1273],{"class":50},[36,1706,1707,1709,1711,1714,1717],{"class":38,"line":71},[36,1708,463],{"class":42},[36,1710,466],{"class":50},[36,1712,1713],{"class":50}," logs",[36,1715,1716],{"class":46}," --tail=50",[36,1718,1719],{"class":50}," web\n",[36,1721,1722,1724,1726,1728,1730],{"class":38,"line":90},[36,1723,463],{"class":42},[36,1725,466],{"class":50},[36,1727,1713],{"class":50},[36,1729,1716],{"class":46},[36,1731,1732],{"class":50}," nginx\n",[36,1734,1735,1737,1739],{"class":38,"line":96},[36,1736,481],{"class":42},[36,1738,484],{"class":46},[36,1740,487],{"class":50},[14,1742,1743],{},"Treat the deployment as incomplete until logs are clean and HTTP validation succeeds.",[18,1745,1747],{"id":1746},"common-causes","Common Causes",[518,1749,1750,1759,1773,1792,1804,1815,1825,1831,1841,1851],{},[521,1751,1752,1756,1757],{},[1753,1754,1755],"strong",{},"Wrong Gunicorn app path or module name"," → Gunicorn exits or throws import errors → Fix the command to match the real WSGI entrypoint, such as ",[33,1758,1087],{},[521,1760,1761,1770,1771],{},[1753,1762,1763,1764,1767,1768],{},"Gunicorn bound to ",[33,1765,1766],{},"127.0.0.1"," instead of ",[33,1769,612],{}," → Nginx cannot reach the app container → Bind Gunicorn to ",[33,1772,1400],{},[521,1774,1775,1781,1782,1785,1786,1789,1790],{},[1753,1776,1777,1778,1780],{},"Nginx ",[33,1779,956],{}," points to the wrong host or port"," → Requests return ",[33,1783,1784],{},"502"," or ",[33,1787,1788],{},"connection refused"," → Use the Compose service name, such as ",[33,1791,512],{},[521,1793,1794,1796,1797,1785,1800,1803],{},[1753,1795,532],{}," → App crashes on startup or database connections fail → Define ",[33,1798,1799],{},"env_file",[33,1801,1802],{},"environment"," values and verify them inside the container",[521,1805,1806,1812,1813],{},[1753,1807,1808,1809,1811],{},"Using ",[33,1810,938],{}," on the app container unnecessarily"," → Gunicorn becomes publicly reachable → Remove the public mapping and keep the app internal with ",[33,1814,932],{},[521,1816,1817,1820,1821,1824],{},[1753,1818,1819],{},"Static or media paths are not mounted or served by Nginx"," → CSS, JS, or uploads fail in production → Add the correct volumes and Nginx ",[33,1822,1823],{},"location"," blocks",[521,1826,1827,1830],{},[1753,1828,1829],{},"Container filesystem used for persistent uploads"," → Files disappear after rebuild or replacement → Use named volumes or external storage",[521,1832,1833,1836,1837,1840],{},[1753,1834,1835],{},"Build context or Dockerfile copy paths are wrong"," → Code or dependency files are missing in the image → Fix ",[33,1838,1839],{},"COPY"," instructions and rebuild",[521,1842,1843,1846,1847,1850],{},[1753,1844,1845],{},"Nginx config syntax error"," → Proxy container fails to start → Run ",[33,1848,1849],{},"nginx -t"," inside the container and correct the config",[521,1852,1853,1856],{},[1753,1854,1855],{},"Database starts but app connects too early"," → App fails boot with connection errors → Add retry logic or startup handling for dependent services",[18,1858,1860],{"id":1859},"debugging-section","Debugging Section",[14,1862,1863],{},"Run these commands in order.",[541,1865,1867],{"id":1866},"check-service-state","Check service state",[26,1869,1870],{"className":28,"code":1262,"language":30,"meta":31,"style":31},[33,1871,1872],{"__ignoreMap":31},[36,1873,1874,1876,1878],{"class":38,"line":39},[36,1875,463],{"class":42},[36,1877,466],{"class":50},[36,1879,1273],{"class":50},[14,1881,1453],{},[518,1883,1884,1890,1893],{},[521,1885,1886,1887],{},"containers stuck in ",[33,1888,1889],{},"restarting",[521,1891,1892],{},"exited containers",[521,1894,1895],{},"incorrect published ports on Nginx",[541,1897,1899],{"id":1898},"check-flaskgunicorn-logs","Check Flask\u002FGunicorn logs",[26,1901,1903],{"className":28,"code":1902,"language":30,"meta":31,"style":31},"docker compose logs -f web\n",[33,1904,1905],{"__ignoreMap":31},[36,1906,1907,1909,1911,1913,1916],{"class":38,"line":39},[36,1908,463],{"class":42},[36,1910,466],{"class":50},[36,1912,1713],{"class":50},[36,1914,1915],{"class":46}," -f",[36,1917,1719],{"class":50},[14,1919,1453],{},[518,1921,1922,1927,1930,1933,1936],{},[521,1923,1924],{},[33,1925,1926],{},"ModuleNotFoundError",[521,1928,1929],{},"import failures",[521,1931,1932],{},"missing dependencies",[521,1934,1935],{},"bad Gunicorn app path",[521,1937,1938],{},"database connection exceptions during startup",[541,1940,1942],{"id":1941},"check-nginx-logs","Check Nginx logs",[26,1944,1946],{"className":28,"code":1945,"language":30,"meta":31,"style":31},"docker compose logs -f nginx\n",[33,1947,1948],{"__ignoreMap":31},[36,1949,1950,1952,1954,1956,1958],{"class":38,"line":39},[36,1951,463],{"class":42},[36,1953,466],{"class":50},[36,1955,1713],{"class":50},[36,1957,1915],{"class":46},[36,1959,1732],{"class":50},[14,1961,1453],{},[518,1963,1964,1969,1974,1979],{},[521,1965,1966],{},[33,1967,1968],{},"connect() failed",[521,1970,1971],{},[33,1972,1973],{},"host not found in upstream",[521,1975,1976],{},[33,1977,1978],{},"upstream prematurely closed connection",[521,1980,1981],{},"syntax errors or startup failures",[541,1983,1985],{"id":1984},"inspect-the-web-container","Inspect the web container",[26,1987,1989],{"className":28,"code":1988,"language":30,"meta":31,"style":31},"docker exec -it $(docker compose ps -q web) sh\n",[33,1990,1991],{"__ignoreMap":31},[36,1992,1993,1995,1997,1999,2001,2003,2005,2007,2009,2011,2013],{"class":38,"line":39},[36,1994,463],{"class":42},[36,1996,1108],{"class":50},[36,1998,1111],{"class":46},[36,2000,1114],{"class":54},[36,2002,463],{"class":42},[36,2004,466],{"class":50},[36,2006,1121],{"class":50},[36,2008,1124],{"class":46},[36,2010,1127],{"class":50},[36,2012,1130],{"class":54},[36,2014,2015],{"class":50},"sh\n",[14,2017,2018],{},"Useful checks inside the container:",[26,2020,2022],{"className":28,"code":2021,"language":30,"meta":31,"style":31},"pwd\nls -la\nenv | sort\nps aux\n",[33,2023,2024,2029,2037,2045],{"__ignoreMap":31},[36,2025,2026],{"class":38,"line":39},[36,2027,2028],{"class":46},"pwd\n",[36,2030,2031,2034],{"class":38,"line":64},[36,2032,2033],{"class":42},"ls",[36,2035,2036],{"class":46}," -la\n",[36,2038,2039,2041,2043],{"class":38,"line":71},[36,2040,702],{"class":42},[36,2042,1447],{"class":77},[36,2044,1450],{"class":42},[36,2046,2047,2050],{"class":38,"line":90},[36,2048,2049],{"class":42},"ps",[36,2051,2052],{"class":50}," aux\n",[541,2054,2056],{"id":2055},"test-python-import-manually","Test Python import manually",[26,2058,2059],{"className":28,"code":1099,"language":30,"meta":31,"style":31},[33,2060,2061],{"__ignoreMap":31},[36,2062,2063,2065,2067,2069,2071,2073,2075,2077,2079,2081,2083,2085,2087],{"class":38,"line":39},[36,2064,463],{"class":42},[36,2066,1108],{"class":50},[36,2068,1111],{"class":46},[36,2070,1114],{"class":54},[36,2072,463],{"class":42},[36,2074,466],{"class":50},[36,2076,1121],{"class":50},[36,2078,1124],{"class":46},[36,2080,1127],{"class":50},[36,2082,1130],{"class":54},[36,2084,571],{"class":50},[36,2086,1135],{"class":46},[36,2088,1138],{"class":50},[14,2090,2091],{},"If this fails, your image contents or Gunicorn entrypoint are wrong.",[541,2093,2095],{"id":2094},"validate-nginx-config","Validate Nginx config",[26,2097,2098],{"className":28,"code":1486,"language":30,"meta":31,"style":31},[33,2099,2100],{"__ignoreMap":31},[36,2101,2102,2104,2106,2108,2110,2112,2114,2116,2118,2120,2122,2124],{"class":38,"line":39},[36,2103,463],{"class":42},[36,2105,1108],{"class":50},[36,2107,1111],{"class":46},[36,2109,1114],{"class":54},[36,2111,463],{"class":42},[36,2113,466],{"class":50},[36,2115,1121],{"class":50},[36,2117,1124],{"class":46},[36,2119,1375],{"class":50},[36,2121,1130],{"class":54},[36,2123,962],{"class":50},[36,2125,1515],{"class":46},[541,2127,2129],{"id":2128},"test-nginx-to-web-connectivity","Test Nginx-to-web connectivity",[26,2131,2132],{"className":28,"code":1352,"language":30,"meta":31,"style":31},[33,2133,2134],{"__ignoreMap":31},[36,2135,2136,2138,2140,2142,2144,2146,2148,2150,2152,2154,2156,2158,2160],{"class":38,"line":39},[36,2137,463],{"class":42},[36,2139,1108],{"class":50},[36,2141,1111],{"class":46},[36,2143,1114],{"class":54},[36,2145,463],{"class":42},[36,2147,466],{"class":50},[36,2149,1121],{"class":50},[36,2151,1124],{"class":46},[36,2153,1375],{"class":50},[36,2155,1130],{"class":54},[36,2157,1380],{"class":50},[36,2159,1383],{"class":46},[36,2161,1386],{"class":50},[14,2163,2164],{},"If this fails but the web container is running, verify:",[518,2166,2167,2170,2175],{},[521,2168,2169],{},"Gunicorn bind address",[521,2171,2172,2173],{},"service name in ",[33,2174,737],{},[521,2176,2177],{},"correct internal port",[18,2179,2181],{"id":2180},"checklist","Checklist",[518,2183,2186,2195,2207,2213,2219,2227,2233,2243],{"className":2184},[2185],"contains-task-list",[521,2187,2190,2194],{"className":2188},[2189],"task-list-item",[2191,2192],"input",{"disabled":67,"type":2193},"checkbox"," Flask app starts under Gunicorn without import or dependency errors",[521,2196,2198,2200,2201,2203,2204,2206],{"className":2197},[2189],[2191,2199],{"disabled":67,"type":2193}," Gunicorn binds to ",[33,2202,1400],{}," inside the ",[33,2205,508],{}," container",[521,2208,2210,2212],{"className":2209},[2189],[2191,2211],{"disabled":67,"type":2193}," Nginx proxies to the correct Compose service name and port",[521,2214,2216,2218],{"className":2215},[2189],[2191,2217],{"disabled":67,"type":2193}," Only Nginx exposes public ports",[521,2220,2222,2224,2225,2206],{"className":2221},[2189],[2191,2223],{"disabled":67,"type":2193}," Required environment variables are present in the running ",[33,2226,508],{},[521,2228,2230,2232],{"className":2229},[2189],[2191,2231],{"disabled":67,"type":2193}," Persistent data paths use volumes and are not stored only in the container filesystem",[521,2234,2236,2238,2239,2242],{"className":2235},[2189],[2191,2237],{"disabled":67,"type":2193}," ",[33,2240,2241],{},"docker compose ps"," shows stable running containers with no crash loop",[521,2244,2246,2238,2248,2250],{"className":2245},[2189],[2191,2247],{"disabled":67,"type":2193},[33,2249,481],{}," to the server returns the expected response through Nginx",[18,2252,2254],{"id":2253},"related-guides","Related Guides",[518,2256,2257,2263,2269,2273],{},[521,2258,2259],{},[1053,2260,2262],{"href":2261},"\u002Fdeploy\u002Fdeploy-flask-with-nginx-plus-gunicorn-step-by-step-guide","Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide)",[521,2264,2265],{},[1053,2266,2268],{"href":2267},"\u002Fdeploy\u002Fflask-plus-docker-production-setup-complete-guide","Flask + Docker Production Setup (Complete Guide)",[521,2270,2271],{},[1053,2272,1341],{"href":1340},[521,2274,2275],{},[1053,2276,2278],{"href":2277},"\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do","Flask Production Checklist (Everything You Must Do)",[18,2280,2282],{"id":2281},"faq","FAQ",[541,2284,2286],{"id":2285},"should-flask-run-with-the-built-in-development-server-in-docker-production","Should Flask run with the built-in development server in Docker production?",[14,2288,2289],{},"No. Use Gunicorn or another production WSGI server behind Nginx.",[541,2291,2293],{"id":2292},"can-i-skip-nginx-and-publish-gunicorn-directly","Can I skip Nginx and publish Gunicorn directly?",[14,2295,2296],{},"You can, but Nginx is preferred for reverse proxying, static files, buffering, and TLS termination.",[541,2298,2300],{"id":2299},"how-do-i-persist-uploaded-files","How do I persist uploaded files?",[14,2302,2303],{},"Mount a named volume or use external storage, then configure your app and Nginx to use that path.",[541,2305,2307],{"id":2306},"where-should-https-be-configured","Where should HTTPS be configured?",[14,2309,2310],{},"Usually at Nginx, with certificates mounted into the proxy container or terminated at the host or load balancer.",[541,2312,2314,2315,2318],{"id":2313},"why-does-docker-compose-up-work-locally-but-fail-on-the-server","Why does ",[33,2316,2317],{},"docker compose up"," work locally but fail on the server?",[14,2320,2321],{},"Typical causes are missing environment variables, wrong paths, port conflicts, DNS differences, firewall rules, or server-specific filesystem assumptions.",[18,2323,2325],{"id":2324},"final-takeaway","Final Takeaway",[14,2327,2328],{},"A reliable Flask Docker Compose deployment depends on four things: the correct Gunicorn entrypoint, the correct Nginx upstream target, the correct environment variables, and the correct volume and network configuration. Validate those first before investigating deeper application issues.",[2330,2331,2332],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":31,"searchDepth":64,"depth":64,"links":2334},[2335,2336,2337,2354,2355,2364,2365,2366,2374],{"id":20,"depth":64,"text":21},{"id":501,"depth":64,"text":502},{"id":538,"depth":64,"text":539,"children":2338},[2339,2340,2341,2342,2344,2345,2346,2347,2348,2349,2350,2351,2352,2353],{"id":543,"depth":71,"text":544},{"id":605,"depth":71,"text":606},{"id":690,"depth":71,"text":691},{"id":733,"depth":71,"text":2343},"4. Configure docker-compose.yml",{"id":945,"depth":71,"text":946},{"id":1060,"depth":71,"text":1061},{"id":1141,"depth":71,"text":1142},{"id":1235,"depth":71,"text":1236},{"id":1289,"depth":71,"text":1290},{"id":1345,"depth":71,"text":1346},{"id":1411,"depth":71,"text":1412},{"id":1479,"depth":71,"text":1480},{"id":1526,"depth":71,"text":1527},{"id":1675,"depth":71,"text":1676},{"id":1746,"depth":64,"text":1747},{"id":1859,"depth":64,"text":1860,"children":2356},[2357,2358,2359,2360,2361,2362,2363],{"id":1866,"depth":71,"text":1867},{"id":1898,"depth":71,"text":1899},{"id":1941,"depth":71,"text":1942},{"id":1984,"depth":71,"text":1985},{"id":2055,"depth":71,"text":2056},{"id":2094,"depth":71,"text":2095},{"id":2128,"depth":71,"text":2129},{"id":2180,"depth":64,"text":2181},{"id":2253,"depth":64,"text":2254},{"id":2281,"depth":64,"text":2282,"children":2367},[2368,2369,2370,2371,2372],{"id":2285,"depth":71,"text":2286},{"id":2292,"depth":71,"text":2293},{"id":2299,"depth":71,"text":2300},{"id":2306,"depth":71,"text":2307},{"id":2313,"depth":71,"text":2373},"Why does docker compose up work locally but fail on the server?",{"id":2324,"depth":64,"text":2325},"Complete guide on flask + docker compose production setup for Flask production environments.","md",{"ogTitle":5,"ogDescription":2375,"twitterCard":2378,"robots":2379,"canonical":2380},"summary_large_image","index, follow","https:\u002F\u002Fflask-deployment.com\u002Fdeploy\u002Fflask-plus-docker-compose-production-setup","\u002Fdeploy\u002Fflask-plus-docker-compose-production-setup",{"title":5,"description":2375},"deploy\u002Fflask-plus-docker-compose-production-setup","e2R0wVKuPR-dafivTBP19RgX1DjukL2mSwwQCrzd18U",1776805765081]