[{"data":1,"prerenderedAt":2123},["ShallowReactive",2],{"\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do":3},{"id":4,"title":5,"body":6,"description":2113,"extension":2114,"meta":2115,"navigation":94,"path":2119,"seo":2120,"stem":2121,"__hash__":2122},"content\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do.md","Flask Production Checklist (Everything You Must Do)",{"type":7,"value":8,"toc":2102},"minimark",[9,13,17,22,25,265,268,272,275,278,281,285,886,889,914,917,955,962,979,986,989,1011,1020,1027,1030,1033,1052,1055,1069,1076,1104,1111,1114,1117,1126,1129,1136,1139,1141,1200,1207,1210,1249,1252,1259,1262,1315,1317,1331,1338,1341,1359,1366,1369,1380,1383,1390,1393,1396,1408,1411,1419,1422,1445,1453,1456,1476,1484,1487,1491,1569,1573,1576,1865,1868,1896,1903,1907,2014,2018,2040,2044,2052,2060,2068,2080,2088,2092,2095,2098],[10,11,5],"h1",{"id":12},"flask-production-checklist-everything-you-must-do",[14,15,16],"p",{},"If you're preparing a Flask app for production or validating an existing deployment, this guide shows you what must be configured and verified. Use it to catch common deployment gaps before they cause downtime, security issues, or broken app behavior.",[18,19,21],"h2",{"id":20},"quick-fix-quick-setup","Quick Fix \u002F Quick Setup",[14,23,24],{},"Run this fast validation pass on the server:",[26,27,32],"pre",{"className":28,"code":29,"language":30,"meta":31,"style":31},"language-bash shiki shiki-themes github-light github-dark","# Validate Nginx and service state\nsudo nginx -t\nsudo systemctl status gunicorn --no-pager\nsudo systemctl status nginx --no-pager\n\n# Test Gunicorn directly\ncurl -I http:\u002F\u002F127.0.0.1:8000 || curl --unix-socket \u002Frun\u002Fgunicorn.sock http:\u002F\u002Flocalhost\u002F\n\n# Test public HTTPS\ncurl -I https:\u002F\u002Fyour-domain.com\n\n# Check recent logs\njournalctl -u gunicorn -n 50 --no-pager\njournalctl -u nginx -n 50 --no-pager\n\n# Verify service environment and migration state\nsudo systemctl show gunicorn --property=Environment\nsource \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate && flask db current\n\n# Verify static file delivery\ncurl -I https:\u002F\u002Fyour-domain.com\u002Fstatic\u002Fapp.css\n","bash","",[33,34,35,44,59,76,89,96,102,130,135,141,151,156,162,181,196,201,207,222,244,249,255],"code",{"__ignoreMap":31},[36,37,40],"span",{"class":38,"line":39},"line",1,[36,41,43],{"class":42},"sJ8bj","# Validate Nginx and service state\n",[36,45,47,51,55],{"class":38,"line":46},2,[36,48,50],{"class":49},"sScJk","sudo",[36,52,54],{"class":53},"sZZnC"," nginx",[36,56,58],{"class":57},"sj4cs"," -t\n",[36,60,62,64,67,70,73],{"class":38,"line":61},3,[36,63,50],{"class":49},[36,65,66],{"class":53}," systemctl",[36,68,69],{"class":53}," status",[36,71,72],{"class":53}," gunicorn",[36,74,75],{"class":57}," --no-pager\n",[36,77,79,81,83,85,87],{"class":38,"line":78},4,[36,80,50],{"class":49},[36,82,66],{"class":53},[36,84,69],{"class":53},[36,86,54],{"class":53},[36,88,75],{"class":57},[36,90,92],{"class":38,"line":91},5,[36,93,95],{"emptyLinePlaceholder":94},true,"\n",[36,97,99],{"class":38,"line":98},6,[36,100,101],{"class":42},"# Test Gunicorn directly\n",[36,103,105,108,111,114,118,121,124,127],{"class":38,"line":104},7,[36,106,107],{"class":49},"curl",[36,109,110],{"class":57}," -I",[36,112,113],{"class":53}," http:\u002F\u002F127.0.0.1:8000",[36,115,117],{"class":116},"szBVR"," ||",[36,119,120],{"class":49}," curl",[36,122,123],{"class":57}," --unix-socket",[36,125,126],{"class":53}," \u002Frun\u002Fgunicorn.sock",[36,128,129],{"class":53}," http:\u002F\u002Flocalhost\u002F\n",[36,131,133],{"class":38,"line":132},8,[36,134,95],{"emptyLinePlaceholder":94},[36,136,138],{"class":38,"line":137},9,[36,139,140],{"class":42},"# Test public HTTPS\n",[36,142,144,146,148],{"class":38,"line":143},10,[36,145,107],{"class":49},[36,147,110],{"class":57},[36,149,150],{"class":53}," https:\u002F\u002Fyour-domain.com\n",[36,152,154],{"class":38,"line":153},11,[36,155,95],{"emptyLinePlaceholder":94},[36,157,159],{"class":38,"line":158},12,[36,160,161],{"class":42},"# Check recent logs\n",[36,163,165,168,171,173,176,179],{"class":38,"line":164},13,[36,166,167],{"class":49},"journalctl",[36,169,170],{"class":57}," -u",[36,172,72],{"class":53},[36,174,175],{"class":57}," -n",[36,177,178],{"class":57}," 50",[36,180,75],{"class":57},[36,182,184,186,188,190,192,194],{"class":38,"line":183},14,[36,185,167],{"class":49},[36,187,170],{"class":57},[36,189,54],{"class":53},[36,191,175],{"class":57},[36,193,178],{"class":57},[36,195,75],{"class":57},[36,197,199],{"class":38,"line":198},15,[36,200,95],{"emptyLinePlaceholder":94},[36,202,204],{"class":38,"line":203},16,[36,205,206],{"class":42},"# Verify service environment and migration state\n",[36,208,210,212,214,217,219],{"class":38,"line":209},17,[36,211,50],{"class":49},[36,213,66],{"class":53},[36,215,216],{"class":53}," show",[36,218,72],{"class":53},[36,220,221],{"class":57}," --property=Environment\n",[36,223,225,228,231,235,238,241],{"class":38,"line":224},18,[36,226,227],{"class":57},"source",[36,229,230],{"class":53}," \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate",[36,232,234],{"class":233},"sVt8B"," && ",[36,236,237],{"class":49},"flask",[36,239,240],{"class":53}," db",[36,242,243],{"class":53}," current\n",[36,245,247],{"class":38,"line":246},19,[36,248,95],{"emptyLinePlaceholder":94},[36,250,252],{"class":38,"line":251},20,[36,253,254],{"class":42},"# Verify static file delivery\n",[36,256,258,260,262],{"class":38,"line":257},21,[36,259,107],{"class":49},[36,261,110],{"class":57},[36,263,264],{"class":53}," https:\u002F\u002Fyour-domain.com\u002Fstatic\u002Fapp.css\n",[14,266,267],{},"Use this as a pre-launch or post-deploy validation pass. Replace paths, domain, service names, and socket locations to match your setup.",[18,269,271],{"id":270},"whats-happening","What’s Happening",[14,273,274],{},"A Flask app is production-ready only when the full stack is configured correctly: process manager, app server, reverse proxy, TLS, static\u002Fmedia handling, secrets, database, logs, and recovery controls.",[14,276,277],{},"Most production issues come from one missing layer rather than from Flask itself.",[14,279,280],{},"This checklist validates the full request path from domain to application response.",[18,282,284],{"id":283},"step-by-step-guide","Step-by-Step Guide",[286,287,288,351,390,426,487,624,708,818,848,881],"ol",{},[289,290,291,295,298,299,322,324,325],"li",{},[292,293,294],"strong",{},"Confirm the app runs with production settings only.",[296,297],"br",{},"Activate the correct environment and start the app with production config.",[26,300,302],{"className":28,"code":301,"language":30,"meta":31,"style":31},"source \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate\npython -c \"from yourapp import app; print(app)\"\n",[33,303,304,311],{"__ignoreMap":31},[36,305,306,308],{"class":38,"line":39},[36,307,227],{"class":57},[36,309,310],{"class":53}," \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate\n",[36,312,313,316,319],{"class":38,"line":46},[36,314,315],{"class":49},"python",[36,317,318],{"class":57}," -c",[36,320,321],{"class":53}," \"from yourapp import app; print(app)\"\n",[296,323],{},"Validate that debug mode is off in your config:",[26,326,329],{"className":327,"code":328,"language":315,"meta":31,"style":31},"language-python shiki shiki-themes github-light github-dark","DEBUG = False\nTESTING = False\n",[33,330,331,342],{"__ignoreMap":31},[36,332,333,336,339],{"class":38,"line":39},[36,334,335],{"class":57},"DEBUG",[36,337,338],{"class":116}," =",[36,340,341],{"class":57}," False\n",[36,343,344,347,349],{"class":38,"line":46},[36,345,346],{"class":57},"TESTING",[36,348,338],{"class":116},[36,350,341],{"class":57},[289,352,353,356,358,359,362,363,376,378,379],{},[292,354,355],{},"Disable development mode.",[296,357],{},"Do not rely on ",[33,360,361],{},"FLASK_ENV=development"," or debug mode in production.",[26,364,366],{"className":28,"code":365,"language":30,"meta":31,"style":31},"unset FLASK_ENV\n",[33,367,368],{"__ignoreMap":31},[36,369,370,373],{"class":38,"line":39},[36,371,372],{"class":57},"unset",[36,374,375],{"class":53}," FLASK_ENV\n",[296,377],{},"If using environment variables:",[26,380,384],{"className":381,"code":382,"language":383,"meta":31,"style":31},"language-ini shiki shiki-themes github-light github-dark","FLASK_DEBUG=0\n","ini",[33,385,386],{"__ignoreMap":31},[36,387,388],{"class":38,"line":39},[36,389,382],{},[289,391,392,395,397,398,400,401],{},[292,393,394],{},"Store secrets outside source files.",[296,396],{},"Put secrets in systemd environment settings, an environment file, or a secrets manager.",[296,399],{},"Example systemd override:",[26,402,404],{"className":381,"code":403,"language":383,"meta":31,"style":31},"[Service]\nEnvironment=\"SECRET_KEY=replace-with-real-secret\"\nEnvironment=\"DATABASE_URL=postgresql:\u002F\u002Fuser:pass@db-host\u002Fdbname\"\nEnvironment=\"FLASK_DEBUG=0\"\n",[33,405,406,411,416,421],{"__ignoreMap":31},[36,407,408],{"class":38,"line":39},[36,409,410],{},"[Service]\n",[36,412,413],{"class":38,"line":46},[36,414,415],{},"Environment=\"SECRET_KEY=replace-with-real-secret\"\n",[36,417,418],{"class":38,"line":61},[36,419,420],{},"Environment=\"DATABASE_URL=postgresql:\u002F\u002Fuser:pass@db-host\u002Fdbname\"\n",[36,422,423],{"class":38,"line":78},[36,424,425],{},"Environment=\"FLASK_DEBUG=0\"\n",[289,427,428,431,433,434,459,461,462],{},[292,429,430],{},"Verify the WSGI entry point.",[296,432],{},"Confirm Gunicorn can import the app module.",[26,435,437],{"className":28,"code":436,"language":30,"meta":31,"style":31},"source \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate\ngunicorn --bind 127.0.0.1:8000 wsgi:app\n",[33,438,439,445],{"__ignoreMap":31},[36,440,441,443],{"class":38,"line":39},[36,442,227],{"class":57},[36,444,310],{"class":53},[36,446,447,450,453,456],{"class":38,"line":46},[36,448,449],{"class":49},"gunicorn",[36,451,452],{"class":57}," --bind",[36,454,455],{"class":53}," 127.0.0.1:8000",[36,457,458],{"class":53}," wsgi:app\n",[296,460],{},"Common valid patterns:",[26,463,465],{"className":28,"code":464,"language":30,"meta":31,"style":31},"gunicorn wsgi:app\ngunicorn app:app\ngunicorn 'yourapp:create_app()'\n",[33,466,467,473,480],{"__ignoreMap":31},[36,468,469,471],{"class":38,"line":39},[36,470,449],{"class":49},[36,472,458],{"class":53},[36,474,475,477],{"class":38,"line":46},[36,476,449],{"class":49},[36,478,479],{"class":53}," app:app\n",[36,481,482,484],{"class":38,"line":61},[36,483,449],{"class":49},[36,485,486],{"class":53}," 'yourapp:create_app()'\n",[289,488,489,492,494,495,592,594,595],{},[292,490,491],{},"Create and enable a Gunicorn service.",[296,493],{},"Example systemd unit:",[26,496,498],{"className":381,"code":497,"language":383,"meta":31,"style":31},"# \u002Fetc\u002Fsystemd\u002Fsystem\u002Fgunicorn.service\n[Unit]\nDescription=Gunicorn for Flask app\nAfter=network.target\n\n[Service]\nUser=www-data\nGroup=www-data\nWorkingDirectory=\u002Fvar\u002Fwww\u002Fyourapp\nEnvironmentFile=\u002Fetc\u002Fyourapp.env\nExecStart=\u002Fvar\u002Fwww\u002Fyourapp\u002Fvenv\u002Fbin\u002Fgunicorn \\\n    --workers 3 \\\n    --bind unix:\u002Frun\u002Fgunicorn.sock \\\n    wsgi:app\nRestart=always\nRestartSec=3\n\n[Install]\nWantedBy=multi-user.target\n",[33,499,500,505,510,515,520,524,528,533,538,543,548,553,558,563,568,573,578,582,587],{"__ignoreMap":31},[36,501,502],{"class":38,"line":39},[36,503,504],{},"# \u002Fetc\u002Fsystemd\u002Fsystem\u002Fgunicorn.service\n",[36,506,507],{"class":38,"line":46},[36,508,509],{},"[Unit]\n",[36,511,512],{"class":38,"line":61},[36,513,514],{},"Description=Gunicorn for Flask app\n",[36,516,517],{"class":38,"line":78},[36,518,519],{},"After=network.target\n",[36,521,522],{"class":38,"line":91},[36,523,95],{"emptyLinePlaceholder":94},[36,525,526],{"class":38,"line":98},[36,527,410],{},[36,529,530],{"class":38,"line":104},[36,531,532],{},"User=www-data\n",[36,534,535],{"class":38,"line":132},[36,536,537],{},"Group=www-data\n",[36,539,540],{"class":38,"line":137},[36,541,542],{},"WorkingDirectory=\u002Fvar\u002Fwww\u002Fyourapp\n",[36,544,545],{"class":38,"line":143},[36,546,547],{},"EnvironmentFile=\u002Fetc\u002Fyourapp.env\n",[36,549,550],{"class":38,"line":153},[36,551,552],{},"ExecStart=\u002Fvar\u002Fwww\u002Fyourapp\u002Fvenv\u002Fbin\u002Fgunicorn \\\n",[36,554,555],{"class":38,"line":158},[36,556,557],{},"    --workers 3 \\\n",[36,559,560],{"class":38,"line":164},[36,561,562],{},"    --bind unix:\u002Frun\u002Fgunicorn.sock \\\n",[36,564,565],{"class":38,"line":183},[36,566,567],{},"    wsgi:app\n",[36,569,570],{"class":38,"line":198},[36,571,572],{},"Restart=always\n",[36,574,575],{"class":38,"line":203},[36,576,577],{},"RestartSec=3\n",[36,579,580],{"class":38,"line":209},[36,581,95],{"emptyLinePlaceholder":94},[36,583,584],{"class":38,"line":224},[36,585,586],{},"[Install]\n",[36,588,589],{"class":38,"line":246},[36,590,591],{},"WantedBy=multi-user.target\n",[296,593],{},"Reload and enable:",[26,596,598],{"className":28,"code":597,"language":30,"meta":31,"style":31},"sudo systemctl daemon-reload\nsudo systemctl enable --now gunicorn\n",[33,599,600,609],{"__ignoreMap":31},[36,601,602,604,606],{"class":38,"line":39},[36,603,50],{"class":49},[36,605,66],{"class":53},[36,607,608],{"class":53}," daemon-reload\n",[36,610,611,613,615,618,621],{"class":38,"line":46},[36,612,50],{"class":49},[36,614,66],{"class":53},[36,616,617],{"class":53}," enable",[36,619,620],{"class":57}," --now",[36,622,623],{"class":53}," gunicorn\n",[289,625,626,629,631,632,663,665,666],{},[292,627,628],{},"Confirm Gunicorn is listening on the expected socket or port.",[296,630],{},"For TCP:",[26,633,635],{"className":28,"code":634,"language":30,"meta":31,"style":31},"ss -ltnp | grep ':8000'\ncurl -I http:\u002F\u002F127.0.0.1:8000\n",[33,636,637,654],{"__ignoreMap":31},[36,638,639,642,645,648,651],{"class":38,"line":39},[36,640,641],{"class":49},"ss",[36,643,644],{"class":57}," -ltnp",[36,646,647],{"class":116}," |",[36,649,650],{"class":49}," grep",[36,652,653],{"class":53}," ':8000'\n",[36,655,656,658,660],{"class":38,"line":46},[36,657,107],{"class":49},[36,659,110],{"class":57},[36,661,662],{"class":53}," http:\u002F\u002F127.0.0.1:8000\n",[296,664],{},"For Unix socket:",[26,667,669],{"className":28,"code":668,"language":30,"meta":31,"style":31},"sudo ss -lx | grep gunicorn\nls -la \u002Frun\u002Fgunicorn.sock\ncurl --unix-socket \u002Frun\u002Fgunicorn.sock http:\u002F\u002Flocalhost\u002F\n",[33,670,671,687,698],{"__ignoreMap":31},[36,672,673,675,678,681,683,685],{"class":38,"line":39},[36,674,50],{"class":49},[36,676,677],{"class":53}," ss",[36,679,680],{"class":57}," -lx",[36,682,647],{"class":116},[36,684,650],{"class":49},[36,686,623],{"class":53},[36,688,689,692,695],{"class":38,"line":46},[36,690,691],{"class":49},"ls",[36,693,694],{"class":57}," -la",[36,696,697],{"class":53}," \u002Frun\u002Fgunicorn.sock\n",[36,699,700,702,704,706],{"class":38,"line":61},[36,701,107],{"class":49},[36,703,123],{"class":57},[36,705,126],{"class":53},[36,707,129],{"class":53},[289,709,710,713,715,716],{},[292,711,712],{},"Configure Nginx as the reverse proxy.",[296,714],{},"Example Nginx server block:",[26,717,721],{"className":718,"code":719,"language":720,"meta":31,"style":31},"language-nginx shiki shiki-themes github-light github-dark","server {\n    listen 80;\n    server_name your-domain.com www.your-domain.com;\n\n    location \u002Fstatic\u002F {\n        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F;\n    }\n\n    location \u002Fuploads\u002F {\n        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fuploads\u002F;\n    }\n\n    location \u002F {\n        proxy_pass http:\u002F\u002Funix:\u002Frun\u002Fgunicorn.sock;\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}\n","nginx",[33,722,723,728,733,738,742,747,752,757,761,766,771,775,779,784,789,794,799,804,809,813],{"__ignoreMap":31},[36,724,725],{"class":38,"line":39},[36,726,727],{},"server {\n",[36,729,730],{"class":38,"line":46},[36,731,732],{},"    listen 80;\n",[36,734,735],{"class":38,"line":61},[36,736,737],{},"    server_name your-domain.com www.your-domain.com;\n",[36,739,740],{"class":38,"line":78},[36,741,95],{"emptyLinePlaceholder":94},[36,743,744],{"class":38,"line":91},[36,745,746],{},"    location \u002Fstatic\u002F {\n",[36,748,749],{"class":38,"line":98},[36,750,751],{},"        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F;\n",[36,753,754],{"class":38,"line":104},[36,755,756],{},"    }\n",[36,758,759],{"class":38,"line":132},[36,760,95],{"emptyLinePlaceholder":94},[36,762,763],{"class":38,"line":137},[36,764,765],{},"    location \u002Fuploads\u002F {\n",[36,767,768],{"class":38,"line":143},[36,769,770],{},"        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fuploads\u002F;\n",[36,772,773],{"class":38,"line":153},[36,774,756],{},[36,776,777],{"class":38,"line":158},[36,778,95],{"emptyLinePlaceholder":94},[36,780,781],{"class":38,"line":164},[36,782,783],{},"    location \u002F {\n",[36,785,786],{"class":38,"line":183},[36,787,788],{},"        proxy_pass http:\u002F\u002Funix:\u002Frun\u002Fgunicorn.sock;\n",[36,790,791],{"class":38,"line":198},[36,792,793],{},"        proxy_set_header Host $host;\n",[36,795,796],{"class":38,"line":203},[36,797,798],{},"        proxy_set_header X-Real-IP $remote_addr;\n",[36,800,801],{"class":38,"line":209},[36,802,803],{},"        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n",[36,805,806],{"class":38,"line":224},[36,807,808],{},"        proxy_set_header X-Forwarded-Proto $scheme;\n",[36,810,811],{"class":38,"line":246},[36,812,756],{},[36,814,815],{"class":38,"line":251},[36,816,817],{},"}\n",[289,819,820,823],{},[292,821,822],{},"Validate and reload Nginx only after config passes.",[26,824,826],{"className":28,"code":825,"language":30,"meta":31,"style":31},"sudo nginx -t\nsudo systemctl reload nginx\n",[33,827,828,836],{"__ignoreMap":31},[36,829,830,832,834],{"class":38,"line":39},[36,831,50],{"class":49},[36,833,54],{"class":53},[36,835,58],{"class":57},[36,837,838,840,842,845],{"class":38,"line":46},[36,839,50],{"class":49},[36,841,66],{"class":53},[36,843,844],{"class":53}," reload",[36,846,847],{"class":53}," nginx\n",[289,849,850,853,878,880],{},[292,851,852],{},"Verify DNS points to the correct server.",[26,854,856],{"className":28,"code":855,"language":30,"meta":31,"style":31},"dig +short your-domain.com\ndig +short www.your-domain.com\n",[33,857,858,869],{"__ignoreMap":31},[36,859,860,863,866],{"class":38,"line":39},[36,861,862],{"class":49},"dig",[36,864,865],{"class":53}," +short",[36,867,868],{"class":53}," your-domain.com\n",[36,870,871,873,875],{"class":38,"line":46},[36,872,862],{"class":49},[36,874,865],{"class":53},[36,876,877],{"class":53}," www.your-domain.com\n",[296,879],{},"Confirm the returned IP matches the server.",[289,882,883],{},[292,884,885],{},"Enable HTTPS.",[14,887,888],{},"If using Certbot with Nginx:",[26,890,892],{"className":28,"code":891,"language":30,"meta":31,"style":31},"sudo certbot --nginx -d your-domain.com -d www.your-domain.com\n",[33,893,894],{"__ignoreMap":31},[36,895,896,898,901,904,907,910,912],{"class":38,"line":39},[36,897,50],{"class":49},[36,899,900],{"class":53}," certbot",[36,902,903],{"class":57}," --nginx",[36,905,906],{"class":57}," -d",[36,908,909],{"class":53}," your-domain.com",[36,911,906],{"class":57},[36,913,877],{"class":53},[14,915,916],{},"Test the result:",[26,918,920],{"className":28,"code":919,"language":30,"meta":31,"style":31},"curl -I https:\u002F\u002Fyour-domain.com\nopenssl s_client -connect your-domain.com:443 -servername your-domain.com \u003C\u002Fdev\u002Fnull\n",[33,921,922,930],{"__ignoreMap":31},[36,923,924,926,928],{"class":38,"line":39},[36,925,107],{"class":49},[36,927,110],{"class":57},[36,929,150],{"class":53},[36,931,932,935,938,941,944,947,949,952],{"class":38,"line":46},[36,933,934],{"class":49},"openssl",[36,936,937],{"class":53}," s_client",[36,939,940],{"class":57}," -connect",[36,942,943],{"class":53}," your-domain.com:443",[36,945,946],{"class":57}," -servername",[36,948,909],{"class":53},[36,950,951],{"class":116}," \u003C",[36,953,954],{"class":53},"\u002Fdev\u002Fnull\n",[286,956,957],{"start":153},[289,958,959],{},[292,960,961],{},"Verify certificate renewal.",[26,963,965],{"className":28,"code":964,"language":30,"meta":31,"style":31},"sudo certbot renew --dry-run\n",[33,966,967],{"__ignoreMap":31},[36,968,969,971,973,976],{"class":38,"line":39},[36,970,50],{"class":49},[36,972,900],{"class":53},[36,974,975],{"class":53}," renew",[36,977,978],{"class":57}," --dry-run\n",[286,980,981],{"start":158},[289,982,983],{},[292,984,985],{},"Serve static files directly from Nginx.",[14,987,988],{},"Confirm the static directory exists and contains files:",[26,990,992],{"className":28,"code":991,"language":30,"meta":31,"style":31},"ls -la \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\ncurl -I https:\u002F\u002Fyour-domain.com\u002Fstatic\u002Fapp.css\n",[33,993,994,1003],{"__ignoreMap":31},[36,995,996,998,1000],{"class":38,"line":39},[36,997,691],{"class":49},[36,999,694],{"class":57},[36,1001,1002],{"class":53}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\n",[36,1004,1005,1007,1009],{"class":38,"line":46},[36,1006,107],{"class":49},[36,1008,110],{"class":57},[36,1010,264],{"class":53},[14,1012,1013,1014,1019],{},"If static files fail, see ",[1015,1016,1018],"a",{"href":1017},"\u002Ffix-issues\u002Fflask-static-files-not-loading-in-production","Flask Static Files Not Loading in Production",".",[286,1021,1022],{"start":164},[289,1023,1024],{},[292,1025,1026],{},"Handle media or uploads separately from static files.",[14,1028,1029],{},"Do not mix build-time static assets and runtime uploads.",[14,1031,1032],{},"Example:",[26,1034,1036],{"className":718,"code":1035,"language":720,"meta":31,"style":31},"location \u002Fuploads\u002F {\n    alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fuploads\u002F;\n}\n",[33,1037,1038,1043,1048],{"__ignoreMap":31},[36,1039,1040],{"class":38,"line":39},[36,1041,1042],{},"location \u002Fuploads\u002F {\n",[36,1044,1045],{"class":38,"line":46},[36,1046,1047],{},"    alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fuploads\u002F;\n",[36,1049,1050],{"class":38,"line":61},[36,1051,817],{},[14,1053,1054],{},"Validate:",[26,1056,1058],{"className":28,"code":1057,"language":30,"meta":31,"style":31},"ls -la \u002Fvar\u002Fwww\u002Fyourapp\u002Fuploads\n",[33,1059,1060],{"__ignoreMap":31},[36,1061,1062,1064,1066],{"class":38,"line":39},[36,1063,691],{"class":49},[36,1065,694],{"class":57},[36,1067,1068],{"class":53}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fuploads\n",[286,1070,1071],{"start":183},[289,1072,1073],{},[292,1074,1075],{},"Apply database migrations before serving traffic.",[26,1077,1079],{"className":28,"code":1078,"language":30,"meta":31,"style":31},"source \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate\nflask db upgrade\nflask db current\n",[33,1080,1081,1087,1096],{"__ignoreMap":31},[36,1082,1083,1085],{"class":38,"line":39},[36,1084,227],{"class":57},[36,1086,310],{"class":53},[36,1088,1089,1091,1093],{"class":38,"line":46},[36,1090,237],{"class":49},[36,1092,240],{"class":53},[36,1094,1095],{"class":53}," upgrade\n",[36,1097,1098,1100,1102],{"class":38,"line":61},[36,1099,237],{"class":49},[36,1101,240],{"class":53},[36,1103,243],{"class":53},[286,1105,1106],{"start":198},[289,1107,1108],{},[292,1109,1110],{},"Verify database connectivity from the app host.",[14,1112,1113],{},"Confirm credentials, host, port, TLS, and network access are correct.",[14,1115,1116],{},"Example environment:",[26,1118,1120],{"className":381,"code":1119,"language":383,"meta":31,"style":31},"DATABASE_URL=postgresql:\u002F\u002Fuser:pass@db-host:5432\u002Fdbname\n",[33,1121,1122],{"__ignoreMap":31},[36,1123,1124],{"class":38,"line":39},[36,1125,1119],{},[14,1127,1128],{},"If needed, test with the database client directly.",[286,1130,1131],{"start":203},[289,1132,1133],{},[292,1134,1135],{},"Set production security configuration.",[14,1137,1138],{},"At minimum, use a real secret key and secure cookie settings.",[14,1140,1032],{},[26,1142,1144],{"className":327,"code":1143,"language":315,"meta":31,"style":31},"SECRET_KEY = os.environ[\"SECRET_KEY\"]\nSESSION_COOKIE_SECURE = True\nREMEMBER_COOKIE_SECURE = True\nSESSION_COOKIE_HTTPONLY = True\nSESSION_COOKIE_SAMESITE = \"Lax\"\n",[33,1145,1146,1162,1172,1181,1190],{"__ignoreMap":31},[36,1147,1148,1151,1153,1156,1159],{"class":38,"line":39},[36,1149,1150],{"class":57},"SECRET_KEY",[36,1152,338],{"class":116},[36,1154,1155],{"class":233}," os.environ[",[36,1157,1158],{"class":53},"\"SECRET_KEY\"",[36,1160,1161],{"class":233},"]\n",[36,1163,1164,1167,1169],{"class":38,"line":46},[36,1165,1166],{"class":57},"SESSION_COOKIE_SECURE",[36,1168,338],{"class":116},[36,1170,1171],{"class":57}," True\n",[36,1173,1174,1177,1179],{"class":38,"line":61},[36,1175,1176],{"class":57},"REMEMBER_COOKIE_SECURE",[36,1178,338],{"class":116},[36,1180,1171],{"class":57},[36,1182,1183,1186,1188],{"class":38,"line":78},[36,1184,1185],{"class":57},"SESSION_COOKIE_HTTPONLY",[36,1187,338],{"class":116},[36,1189,1171],{"class":57},[36,1191,1192,1195,1197],{"class":38,"line":91},[36,1193,1194],{"class":57},"SESSION_COOKIE_SAMESITE",[36,1196,338],{"class":116},[36,1198,1199],{"class":53}," \"Lax\"\n",[286,1201,1202],{"start":209},[289,1203,1204],{},[292,1205,1206],{},"Enable persistent logging.",[14,1208,1209],{},"Check Gunicorn and Nginx logs are available and readable.",[26,1211,1213],{"className":28,"code":1212,"language":30,"meta":31,"style":31},"sudo journalctl -u gunicorn -n 100 --no-pager\nsudo journalctl -u nginx -n 100 --no-pager\n",[33,1214,1215,1233],{"__ignoreMap":31},[36,1216,1217,1219,1222,1224,1226,1228,1231],{"class":38,"line":39},[36,1218,50],{"class":49},[36,1220,1221],{"class":53}," journalctl",[36,1223,170],{"class":57},[36,1225,72],{"class":53},[36,1227,175],{"class":57},[36,1229,1230],{"class":57}," 100",[36,1232,75],{"class":57},[36,1234,1235,1237,1239,1241,1243,1245,1247],{"class":38,"line":46},[36,1236,50],{"class":49},[36,1238,1221],{"class":53},[36,1240,170],{"class":57},[36,1242,54],{"class":53},[36,1244,175],{"class":57},[36,1246,1230],{"class":57},[36,1248,75],{"class":57},[14,1250,1251],{},"If using file-based logs, confirm file paths and write permissions.",[286,1253,1254],{"start":224},[289,1255,1256],{},[292,1257,1258],{},"Add a health check endpoint.",[14,1260,1261],{},"Example Flask route:",[26,1263,1265],{"className":327,"code":1264,"language":315,"meta":31,"style":31},"@app.route(\"\u002Fhealth\")\ndef health():\n    return {\"status\": \"ok\"}, 200\n",[33,1266,1267,1281,1292],{"__ignoreMap":31},[36,1268,1269,1272,1275,1278],{"class":38,"line":39},[36,1270,1271],{"class":49},"@app.route",[36,1273,1274],{"class":233},"(",[36,1276,1277],{"class":53},"\"\u002Fhealth\"",[36,1279,1280],{"class":233},")\n",[36,1282,1283,1286,1289],{"class":38,"line":46},[36,1284,1285],{"class":116},"def",[36,1287,1288],{"class":49}," health",[36,1290,1291],{"class":233},"():\n",[36,1293,1294,1297,1300,1303,1306,1309,1312],{"class":38,"line":61},[36,1295,1296],{"class":116},"    return",[36,1298,1299],{"class":233}," {",[36,1301,1302],{"class":53},"\"status\"",[36,1304,1305],{"class":233},": ",[36,1307,1308],{"class":53},"\"ok\"",[36,1310,1311],{"class":233},"}, ",[36,1313,1314],{"class":57},"200\n",[14,1316,1054],{},[26,1318,1320],{"className":28,"code":1319,"language":30,"meta":31,"style":31},"curl -I https:\u002F\u002Fyour-domain.com\u002Fhealth\n",[33,1321,1322],{"__ignoreMap":31},[36,1323,1324,1326,1328],{"class":38,"line":39},[36,1325,107],{"class":49},[36,1327,110],{"class":57},[36,1329,1330],{"class":53}," https:\u002F\u002Fyour-domain.com\u002Fhealth\n",[286,1332,1333],{"start":246},[289,1334,1335],{},[292,1336,1337],{},"Configure monitoring and alerting.",[14,1339,1340],{},"At minimum, monitor:",[1342,1343,1344,1347,1350,1353,1356],"ul",{},[289,1345,1346],{},"uptime",[289,1348,1349],{},"5xx response rate",[289,1351,1352],{},"CPU and memory",[289,1354,1355],{},"disk space",[289,1357,1358],{},"certificate expiry",[286,1360,1361],{"start":251},[289,1362,1363],{},[292,1364,1365],{},"Create backups and verify restore capability.",[14,1367,1368],{},"Back up:",[1342,1370,1371,1374,1377],{},[289,1372,1373],{},"database",[289,1375,1376],{},"uploaded files",[289,1378,1379],{},"critical config",[14,1381,1382],{},"The checklist is incomplete until restore steps are documented and tested.",[286,1384,1385],{"start":257},[289,1386,1387],{},[292,1388,1389],{},"Restrict public network exposure.",[14,1391,1392],{},"Only expose required ports.",[14,1394,1395],{},"Typical public ports:",[1342,1397,1398,1403],{},[289,1399,1400],{},[33,1401,1402],{},"80",[289,1404,1405],{},[33,1406,1407],{},"443",[14,1409,1410],{},"Internal-only services should not be publicly reachable.",[286,1412,1414],{"start":1413},22,[289,1415,1416],{},[292,1417,1418],{},"Run an end-to-end smoke test.",[14,1420,1421],{},"Validate all critical paths:",[1342,1423,1424,1427,1430,1433,1436,1439,1442],{},[289,1425,1426],{},"domain resolves",[289,1428,1429],{},"HTTPS loads",[289,1431,1432],{},"app returns 200\u002Fexpected responses",[289,1434,1435],{},"login works",[289,1437,1438],{},"database reads\u002Fwrites succeed",[289,1440,1441],{},"static assets load",[289,1443,1444],{},"uploads work if used",[286,1446,1448],{"start":1447},23,[289,1449,1450],{},[292,1451,1452],{},"Document operational details.",[14,1454,1455],{},"Record:",[1342,1457,1458,1461,1464,1467,1470,1473],{},[289,1459,1460],{},"deploy path",[289,1462,1463],{},"service names",[289,1465,1466],{},"rollback command",[289,1468,1469],{},"environment file location",[289,1471,1472],{},"log locations",[289,1474,1475],{},"backup location",[286,1477,1479],{"start":1478},24,[289,1480,1481],{},[292,1482,1483],{},"Re-run validation after every deployment.",[14,1485,1486],{},"Repeat the same server, app, static, HTTPS, and log checks after each release.",[18,1488,1490],{"id":1489},"common-causes","Common Causes",[1342,1492,1493,1499,1509,1523,1533,1539,1545,1551,1557,1563],{},[289,1494,1495,1498],{},[292,1496,1497],{},"Gunicorn not running or failing on startup"," → Nginx returns 502 or upstream errors → check systemd status, import path, virtualenv, and logs.",[289,1500,1501,1504,1505,1508],{},[292,1502,1503],{},"Nginx points to the wrong socket or port"," → requests never reach Flask → align ",[33,1506,1507],{},"proxy_pass"," with Gunicorn bind settings.",[289,1510,1511,1514,1515,1518,1519,1522],{},[292,1512,1513],{},"Environment variables missing in the service context"," → app starts with bad config or crashes → define ",[33,1516,1517],{},"Environment"," or ",[33,1520,1521],{},"EnvironmentFile"," in systemd and reload the daemon.",[289,1524,1525,1528,1529,1532],{},[292,1526,1527],{},"Static root misconfigured"," → CSS\u002FJS return 404 even though app loads → map ",[33,1530,1531],{},"\u002Fstatic\u002F"," to the correct filesystem directory in Nginx.",[289,1534,1535,1538],{},[292,1536,1537],{},"HTTPS incomplete or broken after certificate install"," → browser warnings or redirect loops → verify server block order, certificate paths, and redirect logic.",[289,1540,1541,1544],{},[292,1542,1543],{},"Database migrations not applied"," → runtime 500 errors or missing tables → run migrations and confirm current revision.",[289,1546,1547,1550],{},[292,1548,1549],{},"Socket or directory permissions incorrect"," → Gunicorn or Nginx cannot access required files → fix ownership, group membership, and mode bits.",[289,1552,1553,1556],{},[292,1554,1555],{},"DNS not updated or pointing to the wrong host"," → public site fails while localhost works → verify A\u002FAAAA records and name resolution.",[289,1558,1559,1562],{},[292,1560,1561],{},"Uploads stored in a non-served path"," → media appears broken after deploy → configure separate media storage and Nginx alias or object storage.",[289,1564,1565,1568],{},[292,1566,1567],{},"Logs not configured"," → incidents are hard to diagnose → enable journald, Gunicorn access\u002Ferror logs, and Nginx logs.",[18,1570,1572],{"id":1571},"debugging-section","Debugging Section",[14,1574,1575],{},"Check these in order:",[286,1577,1578,1596,1630,1672,1694,1722,1748,1766,1794,1818],{},[289,1579,1580,1583],{},[292,1581,1582],{},"Validate Nginx configuration",[26,1584,1586],{"className":28,"code":1585,"language":30,"meta":31,"style":31},"sudo nginx -t\n",[33,1587,1588],{"__ignoreMap":31},[36,1589,1590,1592,1594],{"class":38,"line":39},[36,1591,50],{"class":49},[36,1593,54],{"class":53},[36,1595,58],{"class":57},[289,1597,1598,1601],{},[292,1599,1600],{},"Check service status",[26,1602,1604],{"className":28,"code":1603,"language":30,"meta":31,"style":31},"sudo systemctl status nginx --no-pager\nsudo systemctl status gunicorn --no-pager\n",[33,1605,1606,1618],{"__ignoreMap":31},[36,1607,1608,1610,1612,1614,1616],{"class":38,"line":39},[36,1609,50],{"class":49},[36,1611,66],{"class":53},[36,1613,69],{"class":53},[36,1615,54],{"class":53},[36,1617,75],{"class":57},[36,1619,1620,1622,1624,1626,1628],{"class":38,"line":46},[36,1621,50],{"class":49},[36,1623,66],{"class":53},[36,1625,69],{"class":53},[36,1627,72],{"class":53},[36,1629,75],{"class":57},[289,1631,1632,1635],{},[292,1633,1634],{},"Read recent logs",[26,1636,1638],{"className":28,"code":1637,"language":30,"meta":31,"style":31},"sudo journalctl -u nginx -n 100 --no-pager\nsudo journalctl -u gunicorn -n 100 --no-pager\n",[33,1639,1640,1656],{"__ignoreMap":31},[36,1641,1642,1644,1646,1648,1650,1652,1654],{"class":38,"line":39},[36,1643,50],{"class":49},[36,1645,1221],{"class":53},[36,1647,170],{"class":57},[36,1649,54],{"class":53},[36,1651,175],{"class":57},[36,1653,1230],{"class":57},[36,1655,75],{"class":57},[36,1657,1658,1660,1662,1664,1666,1668,1670],{"class":38,"line":46},[36,1659,50],{"class":49},[36,1661,1221],{"class":53},[36,1663,170],{"class":57},[36,1665,72],{"class":53},[36,1667,175],{"class":57},[36,1669,1230],{"class":57},[36,1671,75],{"class":57},[289,1673,1674,1677],{},[292,1675,1676],{},"Inspect effective environment variables",[26,1678,1680],{"className":28,"code":1679,"language":30,"meta":31,"style":31},"sudo systemctl show gunicorn --property=Environment\n",[33,1681,1682],{"__ignoreMap":31},[36,1683,1684,1686,1688,1690,1692],{"class":38,"line":39},[36,1685,50],{"class":49},[36,1687,66],{"class":53},[36,1689,216],{"class":53},[36,1691,72],{"class":53},[36,1693,221],{"class":57},[289,1695,1696,1699],{},[292,1697,1698],{},"Test Gunicorn directly",[26,1700,1702],{"className":28,"code":1701,"language":30,"meta":31,"style":31},"curl -I http:\u002F\u002F127.0.0.1:8000\ncurl --unix-socket \u002Frun\u002Fgunicorn.sock http:\u002F\u002Flocalhost\u002F\n",[33,1703,1704,1712],{"__ignoreMap":31},[36,1705,1706,1708,1710],{"class":38,"line":39},[36,1707,107],{"class":49},[36,1709,110],{"class":57},[36,1711,662],{"class":53},[36,1713,1714,1716,1718,1720],{"class":38,"line":46},[36,1715,107],{"class":49},[36,1717,123],{"class":57},[36,1719,126],{"class":53},[36,1721,129],{"class":53},[289,1723,1724,1727],{},[292,1725,1726],{},"Test the public domain",[26,1728,1730],{"className":28,"code":1729,"language":30,"meta":31,"style":31},"curl -I https:\u002F\u002Fyour-domain.com\ncurl -I https:\u002F\u002Fyour-domain.com\u002Fstatic\u002Fapp.css\n",[33,1731,1732,1740],{"__ignoreMap":31},[36,1733,1734,1736,1738],{"class":38,"line":39},[36,1735,107],{"class":49},[36,1737,110],{"class":57},[36,1739,150],{"class":53},[36,1741,1742,1744,1746],{"class":38,"line":46},[36,1743,107],{"class":49},[36,1745,110],{"class":57},[36,1747,264],{"class":53},[289,1749,1750,1753],{},[292,1751,1752],{},"Verify DNS",[26,1754,1756],{"className":28,"code":1755,"language":30,"meta":31,"style":31},"dig +short your-domain.com\n",[33,1757,1758],{"__ignoreMap":31},[36,1759,1760,1762,1764],{"class":38,"line":39},[36,1761,862],{"class":49},[36,1763,865],{"class":53},[36,1765,868],{"class":53},[289,1767,1768,1771],{},[292,1769,1770],{},"Inspect TLS certificate",[26,1772,1774],{"className":28,"code":1773,"language":30,"meta":31,"style":31},"openssl s_client -connect your-domain.com:443 -servername your-domain.com \u003C\u002Fdev\u002Fnull\n",[33,1775,1776],{"__ignoreMap":31},[36,1777,1778,1780,1782,1784,1786,1788,1790,1792],{"class":38,"line":39},[36,1779,934],{"class":49},[36,1781,937],{"class":53},[36,1783,940],{"class":57},[36,1785,943],{"class":53},[36,1787,946],{"class":57},[36,1789,909],{"class":53},[36,1791,951],{"class":116},[36,1793,954],{"class":53},[289,1795,1796,1799],{},[292,1797,1798],{},"Confirm migration state",[26,1800,1802],{"className":28,"code":1801,"language":30,"meta":31,"style":31},"source \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate && flask db current\n",[33,1803,1804],{"__ignoreMap":31},[36,1805,1806,1808,1810,1812,1814,1816],{"class":38,"line":39},[36,1807,227],{"class":57},[36,1809,230],{"class":53},[36,1811,234],{"class":233},[36,1813,237],{"class":49},[36,1815,240],{"class":53},[36,1817,243],{"class":53},[289,1819,1820,1823],{},[292,1821,1822],{},"Check process and file permissions",[26,1824,1826],{"className":28,"code":1825,"language":30,"meta":31,"style":31},"ps aux | grep gunicorn\nls -la \u002Frun\u002Fgunicorn.sock\nls -la \u002Fpath\u002Fto\u002Fapp \u002Fpath\u002Fto\u002Fapp\u002Fstatic \u002Fpath\u002Fto\u002Fapp\u002Fuploads\n",[33,1827,1828,1842,1850],{"__ignoreMap":31},[36,1829,1830,1833,1836,1838,1840],{"class":38,"line":39},[36,1831,1832],{"class":49},"ps",[36,1834,1835],{"class":53}," aux",[36,1837,647],{"class":116},[36,1839,650],{"class":49},[36,1841,623],{"class":53},[36,1843,1844,1846,1848],{"class":38,"line":46},[36,1845,691],{"class":49},[36,1847,694],{"class":57},[36,1849,697],{"class":53},[36,1851,1852,1854,1856,1859,1862],{"class":38,"line":61},[36,1853,691],{"class":49},[36,1855,694],{"class":57},[36,1857,1858],{"class":53}," \u002Fpath\u002Fto\u002Fapp",[36,1860,1861],{"class":53}," \u002Fpath\u002Fto\u002Fapp\u002Fstatic",[36,1863,1864],{"class":53}," \u002Fpath\u002Fto\u002Fapp\u002Fuploads\n",[14,1866,1867],{},"What to look for:",[1342,1869,1870,1873,1876,1881,1884,1887,1890,1893],{},[289,1871,1872],{},"import errors",[289,1874,1875],{},"missing environment variables",[289,1877,1878],{},[33,1879,1880],{},"502 Bad Gateway",[289,1882,1883],{},"socket permission errors",[289,1885,1886],{},"wrong static or upload paths",[289,1888,1889],{},"expired or mismatched certificates",[289,1891,1892],{},"missing migrations",[289,1894,1895],{},"incorrect DNS records",[14,1897,1898,1899,1019],{},"If upstream requests fail, use ",[1015,1900,1902],{"href":1901},"\u002Ffix-issues\u002Ffix-flask-502-bad-gateway-step-by-step-guide","Fix Flask 502 Bad Gateway (Step-by-Step Guide)",[18,1904,1906],{"id":1905},"checklist","Checklist",[1342,1908,1911,1920,1926,1932,1938,1944,1950,1960,1966,1972,1978,1984,1990,1996,2002,2008],{"className":1909},[1910],"contains-task-list",[289,1912,1915,1919],{"className":1913},[1914],"task-list-item",[1916,1917],"input",{"disabled":94,"type":1918},"checkbox"," App runs with production config and debug is disabled.",[289,1921,1923,1925],{"className":1922},[1914],[1916,1924],{"disabled":94,"type":1918}," Gunicorn starts successfully and remains active after restart.",[289,1927,1929,1931],{"className":1928},[1914],[1916,1930],{"disabled":94,"type":1918}," Nginx config passes validation and reloads cleanly.",[289,1933,1935,1937],{"className":1934},[1914],[1916,1936],{"disabled":94,"type":1918}," Domain resolves to the correct server IP.",[289,1939,1941,1943],{"className":1940},[1914],[1916,1942],{"disabled":94,"type":1918}," HTTPS certificate is valid and auto-renewal is configured.",[289,1945,1947,1949],{"className":1946},[1914],[1916,1948],{"disabled":94,"type":1918}," Nginx proxies requests to Gunicorn correctly.",[289,1951,1953,1955,1956,1959],{"className":1952},[1914],[1916,1954],{"disabled":94,"type":1918}," Static files return ",[33,1957,1958],{},"200"," from Nginx.",[289,1961,1963,1965],{"className":1962},[1914],[1916,1964],{"disabled":94,"type":1918}," Media\u002Fuploads are stored and served from the intended path or backend.",[289,1967,1969,1971],{"className":1968},[1914],[1916,1970],{"disabled":94,"type":1918}," Environment variables are loaded by the running service.",[289,1973,1975,1977],{"className":1974},[1914],[1916,1976],{"disabled":94,"type":1918}," Database connection succeeds in production.",[289,1979,1981,1983],{"className":1980},[1914],[1916,1982],{"disabled":94,"type":1918}," Latest migrations are applied.",[289,1985,1987,1989],{"className":1986},[1914],[1916,1988],{"disabled":94,"type":1918}," Logs are writable and readable in a known location.",[289,1991,1993,1995],{"className":1992},[1914],[1916,1994],{"disabled":94,"type":1918}," Monitoring or error tracking is enabled.",[289,1997,1999,2001],{"className":1998},[1914],[1916,2000],{"disabled":94,"type":1918}," Backups exist and restore steps are documented.",[289,2003,2005,2007],{"className":2004},[1914],[1916,2006],{"disabled":94,"type":1918}," Firewall and exposed ports are limited to required services.",[289,2009,2011,2013],{"className":2010},[1914],[1916,2012],{"disabled":94,"type":1918}," A full application smoke test succeeds over the public domain.",[18,2015,2017],{"id":2016},"related-guides","Related Guides",[1342,2019,2020,2026,2030,2034],{},[289,2021,2022],{},[1015,2023,2025],{"href":2024},"\u002Fdeploy\u002Fdeploy-flask-with-nginx-plus-gunicorn-step-by-step-guide","Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide)",[289,2027,2028],{},[1015,2029,1902],{"href":1901},[289,2031,2032],{},[1015,2033,1018],{"href":1017},[289,2035,2036],{},[1015,2037,2039],{"href":2038},"\u002Fchecklist\u002Fflask-deployment-checklist","Flask Deployment Checklist",[18,2041,2043],{"id":2042},"faq","FAQ",[14,2045,2046,2049,2051],{},[292,2047,2048],{},"Q: What are the minimum production components for Flask?",[296,2050],{},"\nA: A production WSGI server, reverse proxy, environment-based secrets, database configuration, HTTPS, logging, and a basic validation process.",[14,2053,2054,2057,2059],{},[292,2055,2056],{},"Q: Can I use this checklist for Docker deployments too?",[296,2058],{},"\nA: Yes, but replace systemd checks with container health, logs, networking, volumes, and restart policy checks.",[14,2061,2062,2065,2067],{},[292,2063,2064],{},"Q: How often should I run this checklist?",[296,2066],{},"\nA: Before launch, after every deployment, after infrastructure changes, and during incident review.",[14,2069,2070,2073,2075,2076,2079],{},[292,2071,2072],{},"Q: What should I verify first if the site is down?",[296,2074],{},"\nA: Check ",[33,2077,2078],{},"nginx -t",", Nginx and Gunicorn service status, then inspect recent logs and test the local upstream response.",[14,2081,2082,2085,2087],{},[292,2083,2084],{},"Q: Is HTTPS optional for production?",[296,2086],{},"\nA: No for public applications. HTTPS should be treated as a standard production requirement.",[18,2089,2091],{"id":2090},"final-takeaway","Final Takeaway",[14,2093,2094],{},"A Flask production deployment is only complete when the entire stack is validated, not just when the app starts.",[14,2096,2097],{},"Use this checklist to verify runtime, proxying, HTTPS, files, database, observability, and recovery before calling the deployment done.",[2099,2100,2101],"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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":31,"searchDepth":46,"depth":46,"links":2103},[2104,2105,2106,2107,2108,2109,2110,2111,2112],{"id":20,"depth":46,"text":21},{"id":270,"depth":46,"text":271},{"id":283,"depth":46,"text":284},{"id":1489,"depth":46,"text":1490},{"id":1571,"depth":46,"text":1572},{"id":1905,"depth":46,"text":1906},{"id":2016,"depth":46,"text":2017},{"id":2042,"depth":46,"text":2043},{"id":2090,"depth":46,"text":2091},"Complete guide on flask production checklist (everything you must do) for Flask production environments.","md",{"ogTitle":5,"ogDescription":2113,"twitterCard":2116,"robots":2117,"canonical":2118},"summary_large_image","index, follow","https:\u002F\u002Fflask-deployment.com\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do","\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do",{"title":5,"description":2113},"checklist\u002Fflask-production-checklist-everything-you-must-do","4Rp4SgNN5dtbsJZT2dT64W9JUPNfGTsRQYCcSrPcjEg",1776805765071]