[{"data":1,"prerenderedAt":1864},["ShallowReactive",2],{"\u002Fchecklist\u002Fflask-deployment-checklist":3},{"id":4,"title":5,"body":6,"description":1854,"extension":1855,"meta":1856,"navigation":443,"path":1860,"seo":1861,"stem":1862,"__hash__":1863},"content\u002Fchecklist\u002Fflask-deployment-checklist.md","Flask Deployment Checklist",{"type":7,"value":8,"toc":1843},"minimark",[9,13,17,22,25,152,155,159,162,165,168,172,1246,1250,1342,1346,1349,1398,1401,1466,1469,1504,1507,1550,1553,1585,1588,1610,1617,1621,1755,1759,1781,1785,1793,1801,1813,1821,1829,1833,1836,1839],[10,11,5],"h1",{"id":12},"flask-deployment-checklist",[14,15,16],"p",{},"If you're deploying a Flask app to production and need a fast validation list, this guide shows you what to verify before going live. Use it to confirm your app is reachable, services start correctly, configuration is production-safe, and common deployment failures are prevented.",[18,19,21],"h2",{"id":20},"quick-fix-quick-setup","Quick Fix \u002F Quick Setup",[14,23,24],{},"Run these checks first:",[26,27,32],"pre",{"className":28,"code":29,"language":30,"meta":31,"style":31},"language-bash shiki shiki-themes github-light github-dark","sudo systemctl status gunicorn\nsudo systemctl status nginx\nsudo nginx -t\ncurl -I http:\u002F\u002F127.0.0.1:8000 || curl --unix-socket \u002Frun\u002Fgunicorn.sock http:\u002F\u002Flocalhost\u002F\ncurl -I https:\u002F\u002Fyour-domain.com\njournalctl -u gunicorn -n 100 --no-pager\njournalctl -u nginx -n 100 --no-pager\n","bash","",[33,34,35,54,66,78,106,116,137],"code",{"__ignoreMap":31},[36,37,40,44,48,51],"span",{"class":38,"line":39},"line",1,[36,41,43],{"class":42},"sScJk","sudo",[36,45,47],{"class":46},"sZZnC"," systemctl",[36,49,50],{"class":46}," status",[36,52,53],{"class":46}," gunicorn\n",[36,55,57,59,61,63],{"class":38,"line":56},2,[36,58,43],{"class":42},[36,60,47],{"class":46},[36,62,50],{"class":46},[36,64,65],{"class":46}," nginx\n",[36,67,69,71,74],{"class":38,"line":68},3,[36,70,43],{"class":42},[36,72,73],{"class":46}," nginx",[36,75,77],{"class":76},"sj4cs"," -t\n",[36,79,81,84,87,90,94,97,100,103],{"class":38,"line":80},4,[36,82,83],{"class":42},"curl",[36,85,86],{"class":76}," -I",[36,88,89],{"class":46}," http:\u002F\u002F127.0.0.1:8000",[36,91,93],{"class":92},"szBVR"," ||",[36,95,96],{"class":42}," curl",[36,98,99],{"class":76}," --unix-socket",[36,101,102],{"class":46}," \u002Frun\u002Fgunicorn.sock",[36,104,105],{"class":46}," http:\u002F\u002Flocalhost\u002F\n",[36,107,109,111,113],{"class":38,"line":108},5,[36,110,83],{"class":42},[36,112,86],{"class":76},[36,114,115],{"class":46}," https:\u002F\u002Fyour-domain.com\n",[36,117,119,122,125,128,131,134],{"class":38,"line":118},6,[36,120,121],{"class":42},"journalctl",[36,123,124],{"class":76}," -u",[36,126,127],{"class":46}," gunicorn",[36,129,130],{"class":76}," -n",[36,132,133],{"class":76}," 100",[36,135,136],{"class":76}," --no-pager\n",[36,138,140,142,144,146,148,150],{"class":38,"line":139},7,[36,141,121],{"class":42},[36,143,124],{"class":76},[36,145,73],{"class":46},[36,147,130],{"class":76},[36,149,133],{"class":76},[36,151,136],{"class":76},[14,153,154],{},"If Gunicorn is active, Nginx config passes, the app responds locally, and the public domain returns the expected status over HTTPS, the deployment is usually functional.",[18,156,158],{"id":157},"whats-happening","What’s Happening",[14,160,161],{},"A Flask production deployment depends on multiple layers working together: application config, Python environment, Gunicorn, systemd, Nginx, database access, static file handling, DNS, and HTTPS.",[14,163,164],{},"A failure in any layer can present as 502 errors, missing assets, app startup failures, or intermittent downtime.",[14,166,167],{},"This checklist validates the full request path from process startup to public traffic.",[18,169,171],{"id":170},"step-by-step-guide","Step-by-Step Guide",[173,174,175,217,265,297,347,392,501,545,603,645,677,777,810,847,889,938,960,1004,1049,1095,1144,1190,1218],"ol",{},[176,177,178,182,185,186],"li",{},[179,180,181],"strong",{},"Confirm the application is deployed in the expected path",[183,184],"br",{},"Verify the project directory, ownership, and files:",[26,187,189],{"className":28,"code":188,"language":30,"meta":31,"style":31},"ls -lah \u002Fpath\u002Fto\u002Fproject\nsudo chown -R deployuser:deployuser \u002Fpath\u002Fto\u002Fproject\n",[33,190,191,202],{"__ignoreMap":31},[36,192,193,196,199],{"class":38,"line":39},[36,194,195],{"class":42},"ls",[36,197,198],{"class":76}," -lah",[36,200,201],{"class":46}," \u002Fpath\u002Fto\u002Fproject\n",[36,203,204,206,209,212,215],{"class":38,"line":56},[36,205,43],{"class":42},[36,207,208],{"class":46}," chown",[36,210,211],{"class":76}," -R",[36,213,214],{"class":46}," deployuser:deployuser",[36,216,201],{"class":46},[176,218,219,222,224,225],{},[179,220,221],{},"Verify the virtual environment and installed packages",[183,223],{},"Activate the environment and check package health:",[26,226,228],{"className":28,"code":227,"language":30,"meta":31,"style":31},"source \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate\npip check\npip freeze | grep -E 'Flask|gunicorn'\n",[33,229,230,238,246],{"__ignoreMap":31},[36,231,232,235],{"class":38,"line":39},[36,233,234],{"class":76},"source",[36,236,237],{"class":46}," \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate\n",[36,239,240,243],{"class":38,"line":56},[36,241,242],{"class":42},"pip",[36,244,245],{"class":46}," check\n",[36,247,248,250,253,256,259,262],{"class":38,"line":68},[36,249,242],{"class":42},[36,251,252],{"class":46}," freeze",[36,254,255],{"class":92}," |",[36,257,258],{"class":42}," grep",[36,260,261],{"class":76}," -E",[36,263,264],{"class":46}," 'Flask|gunicorn'\n",[176,266,267,270,272,273,275,276,285,287,288],{},[179,268,269],{},"Confirm the Gunicorn app entry point",[183,271],{},"Make sure the configured target matches your app structure.",[183,274],{},"Examples:",[26,277,279],{"className":28,"code":278,"language":30,"meta":31,"style":31},"wsgi:app\n",[33,280,281],{"__ignoreMap":31},[36,282,283],{"class":38,"line":39},[36,284,278],{"class":42},[183,286],{},"or:",[26,289,291],{"className":28,"code":290,"language":30,"meta":31,"style":31},"'wsgi:create_app()'\n",[33,292,293],{"__ignoreMap":31},[36,294,295],{"class":38,"line":39},[36,296,290],{"class":42},[176,298,299,302,304,305,330,332,333],{},[179,300,301],{},"Test Gunicorn manually before using systemd or Nginx",[183,303],{},"Start the app directly:",[26,306,308],{"className":28,"code":307,"language":30,"meta":31,"style":31},"source \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate\ngunicorn -b 127.0.0.1:8000 wsgi:app\n",[33,309,310,316],{"__ignoreMap":31},[36,311,312,314],{"class":38,"line":39},[36,313,234],{"class":76},[36,315,237],{"class":46},[36,317,318,321,324,327],{"class":38,"line":56},[36,319,320],{"class":42},"gunicorn",[36,322,323],{"class":76}," -b",[36,325,326],{"class":46}," 127.0.0.1:8000",[36,328,329],{"class":46}," wsgi:app\n",[183,331],{},"In another terminal:",[26,334,336],{"className":28,"code":335,"language":30,"meta":31,"style":31},"curl -I http:\u002F\u002F127.0.0.1:8000\n",[33,337,338],{"__ignoreMap":31},[36,339,340,342,344],{"class":38,"line":39},[36,341,83],{"class":42},[36,343,86],{"class":76},[36,345,346],{"class":46}," http:\u002F\u002F127.0.0.1:8000\n",[176,348,349,352,354,355,374,376,377],{},[179,350,351],{},"Verify production environment variables",[183,353],{},"Confirm required variables exist:",[26,356,358],{"className":28,"code":357,"language":30,"meta":31,"style":31},"printenv | grep -E 'SECRET_KEY|DATABASE|FLASK|REDIS|API'\n",[33,359,360],{"__ignoreMap":31},[36,361,362,365,367,369,371],{"class":38,"line":39},[36,363,364],{"class":42},"printenv",[36,366,255],{"class":92},[36,368,258],{"class":42},[36,370,261],{"class":76},[36,372,373],{"class":46}," 'SECRET_KEY|DATABASE|FLASK|REDIS|API'\n",[183,375],{},"If using systemd, check the unit file or EnvironmentFile:",[26,378,380],{"className":28,"code":379,"language":30,"meta":31,"style":31},"systemctl cat gunicorn\n",[33,381,382],{"__ignoreMap":31},[36,383,384,387,390],{"class":38,"line":39},[36,385,386],{"class":42},"systemctl",[36,388,389],{"class":46}," cat",[36,391,53],{"class":46},[176,393,394,397,399,400,403,404,403,407,403,410,413,414,416,417],{},[179,395,396],{},"Validate the systemd service file",[183,398],{},"Confirm ",[33,401,402],{},"WorkingDirectory",", ",[33,405,406],{},"ExecStart",[33,408,409],{},"User",[33,411,412],{},"Group",", and environment settings are correct.",[183,415],{},"Example:",[26,418,422],{"className":419,"code":420,"language":421,"meta":31,"style":31},"language-ini shiki shiki-themes github-light github-dark","[Unit]\nDescription=Gunicorn for Flask app\nAfter=network.target\n\n[Service]\nUser=www-data\nGroup=www-data\nWorkingDirectory=\u002Fpath\u002Fto\u002Fproject\nEnvironment=\"PATH=\u002Fpath\u002Fto\u002Fvenv\u002Fbin\"\nEnvironmentFile=\u002Fetc\u002Fdefault\u002Fmyflaskapp\nExecStart=\u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Fgunicorn --workers 3 --bind unix:\u002Frun\u002Fgunicorn.sock wsgi:app\n\n[Install]\nWantedBy=multi-user.target\n","ini",[33,423,424,429,434,439,445,450,455,460,466,472,478,484,489,495],{"__ignoreMap":31},[36,425,426],{"class":38,"line":39},[36,427,428],{},"[Unit]\n",[36,430,431],{"class":38,"line":56},[36,432,433],{},"Description=Gunicorn for Flask app\n",[36,435,436],{"class":38,"line":68},[36,437,438],{},"After=network.target\n",[36,440,441],{"class":38,"line":80},[36,442,444],{"emptyLinePlaceholder":443},true,"\n",[36,446,447],{"class":38,"line":108},[36,448,449],{},"[Service]\n",[36,451,452],{"class":38,"line":118},[36,453,454],{},"User=www-data\n",[36,456,457],{"class":38,"line":139},[36,458,459],{},"Group=www-data\n",[36,461,463],{"class":38,"line":462},8,[36,464,465],{},"WorkingDirectory=\u002Fpath\u002Fto\u002Fproject\n",[36,467,469],{"class":38,"line":468},9,[36,470,471],{},"Environment=\"PATH=\u002Fpath\u002Fto\u002Fvenv\u002Fbin\"\n",[36,473,475],{"class":38,"line":474},10,[36,476,477],{},"EnvironmentFile=\u002Fetc\u002Fdefault\u002Fmyflaskapp\n",[36,479,481],{"class":38,"line":480},11,[36,482,483],{},"ExecStart=\u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Fgunicorn --workers 3 --bind unix:\u002Frun\u002Fgunicorn.sock wsgi:app\n",[36,485,487],{"class":38,"line":486},12,[36,488,444],{"emptyLinePlaceholder":443},[36,490,492],{"class":38,"line":491},13,[36,493,494],{},"[Install]\n",[36,496,498],{"class":38,"line":497},14,[36,499,500],{},"WantedBy=multi-user.target\n",[176,502,503,506,508,509],{},[179,504,505],{},"Reload systemd and restart Gunicorn",[183,507],{},"Apply changes and enable startup on boot:",[26,510,512],{"className":28,"code":511,"language":30,"meta":31,"style":31},"sudo systemctl daemon-reload\nsudo systemctl restart gunicorn\nsudo systemctl enable gunicorn\n",[33,513,514,523,534],{"__ignoreMap":31},[36,515,516,518,520],{"class":38,"line":39},[36,517,43],{"class":42},[36,519,47],{"class":46},[36,521,522],{"class":46}," daemon-reload\n",[36,524,525,527,529,532],{"class":38,"line":56},[36,526,43],{"class":42},[36,528,47],{"class":46},[36,530,531],{"class":46}," restart",[36,533,53],{"class":46},[36,535,536,538,540,543],{"class":38,"line":68},[36,537,43],{"class":42},[36,539,47],{"class":46},[36,541,542],{"class":46}," enable",[36,544,53],{"class":46},[176,546,547,550,552,553,585,587,588],{},[179,548,549],{},"Check Gunicorn health and logs",[183,551],{},"Validate service state:",[26,554,556],{"className":28,"code":555,"language":30,"meta":31,"style":31},"sudo systemctl status gunicorn\nsudo journalctl -u gunicorn -n 100 --no-pager\n",[33,557,558,568],{"__ignoreMap":31},[36,559,560,562,564,566],{"class":38,"line":39},[36,561,43],{"class":42},[36,563,47],{"class":46},[36,565,50],{"class":46},[36,567,53],{"class":46},[36,569,570,572,575,577,579,581,583],{"class":38,"line":56},[36,571,43],{"class":42},[36,573,574],{"class":46}," journalctl",[36,576,124],{"class":76},[36,578,127],{"class":46},[36,580,130],{"class":76},[36,582,133],{"class":76},[36,584,136],{"class":76},[183,586],{},"Look for:",[589,590,591,594,597,600],"ul",{},[176,592,593],{},"import errors",[176,595,596],{},"missing packages",[176,598,599],{},"bad environment variables",[176,601,602],{},"permission failures",[176,604,605,608,610,611,624,626,627,629,630,642,644],{},[179,606,607],{},"Verify Gunicorn bind target matches Nginx upstream",[183,609],{},"If Gunicorn uses TCP:",[26,612,614],{"className":28,"code":613,"language":30,"meta":31,"style":31},"--bind 127.0.0.1:8000\n",[33,615,616],{"__ignoreMap":31},[36,617,618,621],{"class":38,"line":39},[36,619,620],{"class":42},"--bind",[36,622,623],{"class":46}," 127.0.0.1:8000\n",[183,625],{},"Nginx must proxy to the same target.",[183,628],{},"If Gunicorn uses a Unix socket:",[26,631,633],{"className":28,"code":632,"language":30,"meta":31,"style":31},"--bind unix:\u002Frun\u002Fgunicorn.sock\n",[33,634,635],{"__ignoreMap":31},[36,636,637,639],{"class":38,"line":39},[36,638,620],{"class":42},[36,640,641],{"class":46}," unix:\u002Frun\u002Fgunicorn.sock\n",[183,643],{},"Nginx must use that exact socket path.",[176,646,647,650,652,653],{},[179,648,649],{},"Validate Nginx configuration",[183,651],{},"Test and reload:",[26,654,656],{"className":28,"code":655,"language":30,"meta":31,"style":31},"sudo nginx -t\nsudo systemctl reload nginx\n",[33,657,658,666],{"__ignoreMap":31},[36,659,660,662,664],{"class":38,"line":39},[36,661,43],{"class":42},[36,663,73],{"class":46},[36,665,77],{"class":76},[36,667,668,670,672,675],{"class":38,"line":56},[36,669,43],{"class":42},[36,671,47],{"class":46},[36,673,674],{"class":46}," reload",[36,676,65],{"class":46},[176,678,679,682,684,685,688,689,416,691],{},[179,680,681],{},"Confirm the Nginx server block",[183,683],{},"Verify ",[33,686,687],{},"server_name",", reverse proxy, headers, and static file locations.",[183,690],{},[26,692,696],{"className":693,"code":694,"language":695,"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 \u002Fpath\u002Fto\u002Fproject\u002Fstatic\u002F;\n    }\n\n    location \u002F {\n        proxy_pass http:\u002F\u002F127.0.0.1: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}\n","nginx",[33,697,698,703,708,713,717,722,727,732,736,741,746,751,756,761,766,771],{"__ignoreMap":31},[36,699,700],{"class":38,"line":39},[36,701,702],{},"server {\n",[36,704,705],{"class":38,"line":56},[36,706,707],{},"    listen 80;\n",[36,709,710],{"class":38,"line":68},[36,711,712],{},"    server_name your-domain.com www.your-domain.com;\n",[36,714,715],{"class":38,"line":80},[36,716,444],{"emptyLinePlaceholder":443},[36,718,719],{"class":38,"line":108},[36,720,721],{},"    location \u002Fstatic\u002F {\n",[36,723,724],{"class":38,"line":118},[36,725,726],{},"        alias \u002Fpath\u002Fto\u002Fproject\u002Fstatic\u002F;\n",[36,728,729],{"class":38,"line":139},[36,730,731],{},"    }\n",[36,733,734],{"class":38,"line":462},[36,735,444],{"emptyLinePlaceholder":443},[36,737,738],{"class":38,"line":468},[36,739,740],{},"    location \u002F {\n",[36,742,743],{"class":38,"line":474},[36,744,745],{},"        proxy_pass http:\u002F\u002F127.0.0.1:8000;\n",[36,747,748],{"class":38,"line":480},[36,749,750],{},"        proxy_set_header Host $host;\n",[36,752,753],{"class":38,"line":486},[36,754,755],{},"        proxy_set_header X-Real-IP $remote_addr;\n",[36,757,758],{"class":38,"line":491},[36,759,760],{},"        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n",[36,762,763],{"class":38,"line":497},[36,764,765],{},"        proxy_set_header X-Forwarded-Proto $scheme;\n",[36,767,769],{"class":38,"line":768},15,[36,770,731],{},[36,772,774],{"class":38,"line":773},16,[36,775,776],{},"}\n",[176,778,779,782,784,785],{},[179,780,781],{},"Verify DNS",[183,783],{},"Confirm the domain resolves to the server IP:",[26,786,788],{"className":28,"code":787,"language":30,"meta":31,"style":31},"dig your-domain.com +short\ndig www.your-domain.com +short\n",[33,789,790,801],{"__ignoreMap":31},[36,791,792,795,798],{"class":38,"line":39},[36,793,794],{"class":42},"dig",[36,796,797],{"class":46}," your-domain.com",[36,799,800],{"class":46}," +short\n",[36,802,803,805,808],{"class":38,"line":56},[36,804,794],{"class":42},[36,806,807],{"class":46}," www.your-domain.com",[36,809,800],{"class":46},[176,811,812,815,817,818],{},[179,813,814],{},"Test local HTTP response through Nginx",[183,816],{},"Check Nginx routing locally:",[26,819,821],{"className":28,"code":820,"language":30,"meta":31,"style":31},"curl -I http:\u002F\u002Flocalhost\ncurl -I -H 'Host: your-domain.com' http:\u002F\u002F127.0.0.1\n",[33,822,823,832],{"__ignoreMap":31},[36,824,825,827,829],{"class":38,"line":39},[36,826,83],{"class":42},[36,828,86],{"class":76},[36,830,831],{"class":46}," http:\u002F\u002Flocalhost\n",[36,833,834,836,838,841,844],{"class":38,"line":56},[36,835,83],{"class":42},[36,837,86],{"class":76},[36,839,840],{"class":76}," -H",[36,842,843],{"class":46}," 'Host: your-domain.com'",[36,845,846],{"class":46}," http:\u002F\u002F127.0.0.1\n",[176,848,849,852,854,855],{},[179,850,851],{},"Validate HTTPS",[183,853],{},"Confirm the certificate, TLS listener, and public response:",[26,856,858],{"className":28,"code":857,"language":30,"meta":31,"style":31},"curl -I https:\u002F\u002Fyour-domain.com\nsudo certbot certificates\nsudo systemctl status certbot.timer\n",[33,859,860,868,878],{"__ignoreMap":31},[36,861,862,864,866],{"class":38,"line":39},[36,863,83],{"class":42},[36,865,86],{"class":76},[36,867,115],{"class":46},[36,869,870,872,875],{"class":38,"line":56},[36,871,43],{"class":42},[36,873,874],{"class":46}," certbot",[36,876,877],{"class":46}," certificates\n",[36,879,880,882,884,886],{"class":38,"line":68},[36,881,43],{"class":42},[36,883,47],{"class":46},[36,885,50],{"class":46},[36,887,888],{"class":46}," certbot.timer\n",[176,890,891,894,896,897,919,921,922],{},[179,892,893],{},"Test database connectivity",[183,895],{},"Run an app or migration command inside the production environment:",[26,898,900],{"className":28,"code":899,"language":30,"meta":31,"style":31},"source \u002Fpath\u002Fto\u002Fvenv\u002Fbin\u002Factivate\nflask db current\n",[33,901,902,908],{"__ignoreMap":31},[36,903,904,906],{"class":38,"line":39},[36,905,234],{"class":76},[36,907,237],{"class":46},[36,909,910,913,916],{"class":38,"line":56},[36,911,912],{"class":42},"flask",[36,914,915],{"class":46}," db",[36,917,918],{"class":46}," current\n",[183,920],{},"Or test from Python:",[26,923,925],{"className":28,"code":924,"language":30,"meta":31,"style":31},"python -c \"from yourapp import app; print('app import ok')\"\n",[33,926,927],{"__ignoreMap":31},[36,928,929,932,935],{"class":38,"line":39},[36,930,931],{"class":42},"python",[36,933,934],{"class":76}," -c",[36,936,937],{"class":46}," \"from yourapp import app; print('app import ok')\"\n",[176,939,940,943,945,946],{},[179,941,942],{},"Apply pending migrations",[183,944],{},"Use the project’s migration command before launch:",[26,947,949],{"className":28,"code":948,"language":30,"meta":31,"style":31},"flask db upgrade\n",[33,950,951],{"__ignoreMap":31},[36,952,953,955,957],{"class":38,"line":39},[36,954,912],{"class":42},[36,956,915],{"class":46},[36,958,959],{"class":46}," upgrade\n",[176,961,962,965,967,968,971,972,995,997,998,1003],{},[179,963,964],{},"Verify static files",[183,966],{},"Confirm the Nginx ",[33,969,970],{},"alias"," path matches the filesystem and files are readable:",[26,973,975],{"className":28,"code":974,"language":30,"meta":31,"style":31},"ls -lah \u002Fpath\u002Fto\u002Fproject\u002Fstatic\ncurl -I https:\u002F\u002Fyour-domain.com\u002Fstatic\u002Fapp.css\n",[33,976,977,986],{"__ignoreMap":31},[36,978,979,981,983],{"class":38,"line":39},[36,980,195],{"class":42},[36,982,198],{"class":76},[36,984,985],{"class":46}," \u002Fpath\u002Fto\u002Fproject\u002Fstatic\n",[36,987,988,990,992],{"class":38,"line":56},[36,989,83],{"class":42},[36,991,86],{"class":76},[36,993,994],{"class":46}," https:\u002F\u002Fyour-domain.com\u002Fstatic\u002Fapp.css\n",[183,996],{},"If static files are missing, see ",[999,1000,1002],"a",{"href":1001},"\u002Ffix-issues\u002Fflask-static-files-not-loading-in-production","Flask Static Files Not Loading in Production",".",[176,1005,1006,1009,1011,1012],{},[179,1007,1008],{},"Verify media or upload directories",[183,1010],{},"Ensure directories exist and are writable by the application user:",[26,1013,1015],{"className":28,"code":1014,"language":30,"meta":31,"style":31},"mkdir -p \u002Fpath\u002Fto\u002Fproject\u002Fuploads\nsudo chown -R www-data:www-data \u002Fpath\u002Fto\u002Fproject\u002Fuploads\nls -lah \u002Fpath\u002Fto\u002Fproject\u002Fuploads\n",[33,1016,1017,1028,1041],{"__ignoreMap":31},[36,1018,1019,1022,1025],{"class":38,"line":39},[36,1020,1021],{"class":42},"mkdir",[36,1023,1024],{"class":76}," -p",[36,1026,1027],{"class":46}," \u002Fpath\u002Fto\u002Fproject\u002Fuploads\n",[36,1029,1030,1032,1034,1036,1039],{"class":38,"line":56},[36,1031,43],{"class":42},[36,1033,208],{"class":46},[36,1035,211],{"class":76},[36,1037,1038],{"class":46}," www-data:www-data",[36,1040,1027],{"class":46},[36,1042,1043,1045,1047],{"class":38,"line":68},[36,1044,195],{"class":42},[36,1046,198],{"class":76},[36,1048,1027],{"class":46},[176,1050,1051,1054,1056,1057],{},[179,1052,1053],{},"Check firewall rules",[183,1055],{},"Allow public web traffic and keep internal app ports private:",[26,1058,1060],{"className":28,"code":1059,"language":30,"meta":31,"style":31},"sudo ufw status\nsudo ufw allow 80\u002Ftcp\nsudo ufw allow 443\u002Ftcp\n",[33,1061,1062,1072,1084],{"__ignoreMap":31},[36,1063,1064,1066,1069],{"class":38,"line":39},[36,1065,43],{"class":42},[36,1067,1068],{"class":46}," ufw",[36,1070,1071],{"class":46}," status\n",[36,1073,1074,1076,1078,1081],{"class":38,"line":56},[36,1075,43],{"class":42},[36,1077,1068],{"class":46},[36,1079,1080],{"class":46}," allow",[36,1082,1083],{"class":46}," 80\u002Ftcp\n",[36,1085,1086,1088,1090,1092],{"class":38,"line":68},[36,1087,43],{"class":42},[36,1089,1068],{"class":46},[36,1091,1080],{"class":46},[36,1093,1094],{"class":46}," 443\u002Ftcp\n",[176,1096,1097,1100,1102,1103,1129,1131,1132],{},[179,1098,1099],{},"Confirm services start on boot",[183,1101],{},"Check enabled state:",[26,1104,1106],{"className":28,"code":1105,"language":30,"meta":31,"style":31},"sudo systemctl is-enabled gunicorn\nsudo systemctl is-enabled nginx\n",[33,1107,1108,1119],{"__ignoreMap":31},[36,1109,1110,1112,1114,1117],{"class":38,"line":39},[36,1111,43],{"class":42},[36,1113,47],{"class":46},[36,1115,1116],{"class":46}," is-enabled",[36,1118,53],{"class":46},[36,1120,1121,1123,1125,1127],{"class":38,"line":56},[36,1122,43],{"class":42},[36,1124,47],{"class":46},[36,1126,1116],{"class":46},[36,1128,65],{"class":46},[183,1130],{},"Optional reboot validation:",[26,1133,1135],{"className":28,"code":1134,"language":30,"meta":31,"style":31},"sudo reboot\n",[33,1136,1137],{"__ignoreMap":31},[36,1138,1139,1141],{"class":38,"line":39},[36,1140,43],{"class":42},[36,1142,1143],{"class":46}," reboot\n",[176,1145,1146,1149,1151,1152],{},[179,1147,1148],{},"Verify logging, monitoring, and backups",[183,1150],{},"At minimum, confirm logs are accessible and database backups exist.",[26,1153,1155],{"className":28,"code":1154,"language":30,"meta":31,"style":31},"sudo journalctl -u gunicorn -n 50 --no-pager\nsudo journalctl -u nginx -n 50 --no-pager\n",[33,1156,1157,1174],{"__ignoreMap":31},[36,1158,1159,1161,1163,1165,1167,1169,1172],{"class":38,"line":39},[36,1160,43],{"class":42},[36,1162,574],{"class":46},[36,1164,124],{"class":76},[36,1166,127],{"class":46},[36,1168,130],{"class":76},[36,1170,1171],{"class":76}," 50",[36,1173,136],{"class":76},[36,1175,1176,1178,1180,1182,1184,1186,1188],{"class":38,"line":56},[36,1177,43],{"class":42},[36,1179,574],{"class":46},[36,1181,124],{"class":76},[36,1183,73],{"class":46},[36,1185,130],{"class":76},[36,1187,1171],{"class":76},[36,1189,136],{"class":76},[176,1191,1192,1195,1197,1198],{},[179,1193,1194],{},"Perform an external end-to-end test",[183,1196],{},"Validate:",[589,1199,1200,1203,1206,1209,1212,1215],{},[176,1201,1202],{},"home page",[176,1204,1205],{},"login route",[176,1207,1208],{},"one database-backed route",[176,1210,1211],{},"static assets",[176,1213,1214],{},"form submission",[176,1216,1217],{},"upload flow if used",[176,1219,1220,1223,1225,1226],{},[179,1221,1222],{},"Record final deployment paths",[183,1224],{},"Save these values for maintenance:",[589,1227,1228,1231,1234,1237,1240,1243],{},[176,1229,1230],{},"app directory",[176,1232,1233],{},"virtualenv path",[176,1235,1236],{},"systemd unit path",[176,1238,1239],{},"Nginx site file",[176,1241,1242],{},"socket path or port",[176,1244,1245],{},"domain records",[18,1247,1249],{"id":1248},"common-causes","Common Causes",[589,1251,1252,1261,1271,1289,1298,1304,1310,1316,1322,1328],{},[176,1253,1254,1257,1258,1260],{},[179,1255,1256],{},"Gunicorn service misconfiguration"," → Wrong ",[33,1259,402],{},", bad virtualenv path, or incorrect app module prevents startup → Fix the systemd unit and restart the service.",[176,1262,1263,1266,1267,1270],{},[179,1264,1265],{},"Nginx upstream mismatch"," → Nginx points to the wrong socket or port → Make the Gunicorn bind and ",[33,1268,1269],{},"proxy_pass"," target identical.",[176,1272,1273,1276,1277,1280,1281,1284,1285,1003],{},[179,1274,1275],{},"Environment variables missing"," → App starts with missing secrets or database settings and fails at import or runtime → Load variables via systemd ",[33,1278,1279],{},"Environment="," or ",[33,1282,1283],{},"EnvironmentFile="," and restart Gunicorn. See ",[999,1286,1288],{"href":1287},"\u002Ffix-issues\u002Fflask-environment-variables-not-loading-in-production","Flask Environment Variables Not Loading in Production",[176,1290,1291,1294,1295,1297],{},[179,1292,1293],{},"Static files not served"," → Nginx ",[33,1296,970],{}," path does not match the actual static directory → Correct the filesystem path and reload Nginx.",[176,1299,1300,1303],{},[179,1301,1302],{},"Database connection failure"," → Wrong credentials, host, firewall, or missing DB service → Verify connection settings and reachability from the server.",[176,1305,1306,1309],{},[179,1307,1308],{},"Permissions issue on Unix socket"," → Nginx cannot read the Gunicorn socket → Adjust socket location, user\u002Fgroup, and directory permissions.",[176,1311,1312,1315],{},[179,1313,1314],{},"DNS not pointing to the server"," → Public domain never reaches Nginx → Update A\u002FAAAA records and wait for propagation.",[176,1317,1318,1321],{},[179,1319,1320],{},"HTTPS incomplete"," → Certificate exists but Nginx is not serving the right TLS config → Validate certificates, server block, and reload Nginx.",[176,1323,1324,1327],{},[179,1325,1326],{},"Migrations not applied"," → App errors on routes that expect new tables or columns → Run migrations before launch.",[176,1329,1330,1333,1334,1337,1338,1341],{},[179,1331,1332],{},"Firewall blocks traffic"," → Server works locally but not externally → Allow ports ",[33,1335,1336],{},"80"," and ",[33,1339,1340],{},"443"," and verify cloud firewall rules.",[18,1343,1345],{"id":1344},"debugging-section","Debugging Section",[14,1347,1348],{},"Check service and application state:",[26,1350,1352],{"className":28,"code":1351,"language":30,"meta":31,"style":31},"sudo systemctl status gunicorn\nsudo systemctl status nginx\nsudo systemctl daemon-reload\nsystemctl cat gunicorn\nsudo nginx -t\n",[33,1353,1354,1364,1374,1382,1390],{"__ignoreMap":31},[36,1355,1356,1358,1360,1362],{"class":38,"line":39},[36,1357,43],{"class":42},[36,1359,47],{"class":46},[36,1361,50],{"class":46},[36,1363,53],{"class":46},[36,1365,1366,1368,1370,1372],{"class":38,"line":56},[36,1367,43],{"class":42},[36,1369,47],{"class":46},[36,1371,50],{"class":46},[36,1373,65],{"class":46},[36,1375,1376,1378,1380],{"class":38,"line":68},[36,1377,43],{"class":42},[36,1379,47],{"class":46},[36,1381,522],{"class":46},[36,1383,1384,1386,1388],{"class":38,"line":80},[36,1385,386],{"class":42},[36,1387,389],{"class":46},[36,1389,53],{"class":46},[36,1391,1392,1394,1396],{"class":38,"line":108},[36,1393,43],{"class":42},[36,1395,73],{"class":46},[36,1397,77],{"class":76},[14,1399,1400],{},"Check logs:",[26,1402,1404],{"className":28,"code":1403,"language":30,"meta":31,"style":31},"sudo journalctl -u gunicorn -n 200 --no-pager\nsudo journalctl -u nginx -n 200 --no-pager\nsudo tail -n 100 \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\nsudo tail -n 100 \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\n",[33,1405,1406,1423,1439,1453],{"__ignoreMap":31},[36,1407,1408,1410,1412,1414,1416,1418,1421],{"class":38,"line":39},[36,1409,43],{"class":42},[36,1411,574],{"class":46},[36,1413,124],{"class":76},[36,1415,127],{"class":46},[36,1417,130],{"class":76},[36,1419,1420],{"class":76}," 200",[36,1422,136],{"class":76},[36,1424,1425,1427,1429,1431,1433,1435,1437],{"class":38,"line":56},[36,1426,43],{"class":42},[36,1428,574],{"class":46},[36,1430,124],{"class":76},[36,1432,73],{"class":46},[36,1434,130],{"class":76},[36,1436,1420],{"class":76},[36,1438,136],{"class":76},[36,1440,1441,1443,1446,1448,1450],{"class":38,"line":68},[36,1442,43],{"class":42},[36,1444,1445],{"class":46}," tail",[36,1447,130],{"class":76},[36,1449,133],{"class":76},[36,1451,1452],{"class":46}," \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\n",[36,1454,1455,1457,1459,1461,1463],{"class":38,"line":80},[36,1456,43],{"class":42},[36,1458,1445],{"class":46},[36,1460,130],{"class":76},[36,1462,133],{"class":76},[36,1464,1465],{"class":46}," \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\n",[14,1467,1468],{},"Check sockets and listening ports:",[26,1470,1472],{"className":28,"code":1471,"language":30,"meta":31,"style":31},"ss -ltnp | grep -E ':80|:443|:8000'\nss -lx | grep gunicorn\n",[33,1473,1474,1491],{"__ignoreMap":31},[36,1475,1476,1479,1482,1484,1486,1488],{"class":38,"line":39},[36,1477,1478],{"class":42},"ss",[36,1480,1481],{"class":76}," -ltnp",[36,1483,255],{"class":92},[36,1485,258],{"class":42},[36,1487,261],{"class":76},[36,1489,1490],{"class":46}," ':80|:443|:8000'\n",[36,1492,1493,1495,1498,1500,1502],{"class":38,"line":56},[36,1494,1478],{"class":42},[36,1496,1497],{"class":76}," -lx",[36,1499,255],{"class":92},[36,1501,258],{"class":42},[36,1503,53],{"class":46},[14,1505,1506],{},"Test the upstream directly:",[26,1508,1510],{"className":28,"code":1509,"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\ncurl -I -H 'Host: your-domain.com' http:\u002F\u002F127.0.0.1\ncurl -I https:\u002F\u002Fyour-domain.com\n",[33,1511,1512,1520,1530,1542],{"__ignoreMap":31},[36,1513,1514,1516,1518],{"class":38,"line":39},[36,1515,83],{"class":42},[36,1517,86],{"class":76},[36,1519,346],{"class":46},[36,1521,1522,1524,1526,1528],{"class":38,"line":56},[36,1523,83],{"class":42},[36,1525,99],{"class":76},[36,1527,102],{"class":46},[36,1529,105],{"class":46},[36,1531,1532,1534,1536,1538,1540],{"class":38,"line":68},[36,1533,83],{"class":42},[36,1535,86],{"class":76},[36,1537,840],{"class":76},[36,1539,843],{"class":46},[36,1541,846],{"class":46},[36,1543,1544,1546,1548],{"class":38,"line":80},[36,1545,83],{"class":42},[36,1547,86],{"class":76},[36,1549,115],{"class":46},[14,1551,1552],{},"Check DNS and filesystem permissions:",[26,1554,1556],{"className":28,"code":1555,"language":30,"meta":31,"style":31},"dig your-domain.com +short\nls -lah \u002Fpath\u002Fto\u002Fproject\nnamei -l \u002Frun\u002Fgunicorn.sock\n",[33,1557,1558,1566,1574],{"__ignoreMap":31},[36,1559,1560,1562,1564],{"class":38,"line":39},[36,1561,794],{"class":42},[36,1563,797],{"class":46},[36,1565,800],{"class":46},[36,1567,1568,1570,1572],{"class":38,"line":56},[36,1569,195],{"class":42},[36,1571,198],{"class":76},[36,1573,201],{"class":46},[36,1575,1576,1579,1582],{"class":38,"line":68},[36,1577,1578],{"class":42},"namei",[36,1580,1581],{"class":76}," -l",[36,1583,1584],{"class":46}," \u002Frun\u002Fgunicorn.sock\n",[14,1586,1587],{},"What to look for:",[589,1589,1590,1593,1598,1601,1604,1607],{},[176,1591,1592],{},"Gunicorn failing to import the app",[176,1594,1595,1596],{},"wrong module path in ",[33,1597,406],{},[176,1599,1600],{},"Nginx proxying to the wrong port or socket",[176,1602,1603],{},"missing environment variables in the systemd context",[176,1605,1606],{},"unreadable static files or socket path",[176,1608,1609],{},"certificate or DNS mismatches",[14,1611,1612,1613,1003],{},"If the public site returns a 502, go directly to ",[999,1614,1616],{"href":1615},"\u002Ffix-issues\u002Ffix-flask-502-bad-gateway-step-by-step-guide","Fix Flask 502 Bad Gateway (Step-by-Step Guide)",[18,1618,1620],{"id":1619},"checklist","Checklist",[589,1622,1625,1634,1640,1646,1652,1662,1668,1677,1683,1689,1695,1701,1707,1713,1719,1725,1731,1737,1743,1749],{"className":1623},[1624],"contains-task-list",[176,1626,1629,1633],{"className":1627},[1628],"task-list-item",[1630,1631],"input",{"disabled":443,"type":1632},"checkbox"," Application code is deployed in the correct directory.",[176,1635,1637,1639],{"className":1636},[1628],[1630,1638],{"disabled":443,"type":1632}," Virtual environment exists and contains required packages.",[176,1641,1643,1645],{"className":1642},[1628],[1630,1644],{"disabled":443,"type":1632}," Gunicorn starts manually with the configured app entry point.",[176,1647,1649,1651],{"className":1648},[1628],[1630,1650],{"disabled":443,"type":1632}," systemd service uses the correct user, group, working directory, and executable paths.",[176,1653,1655,1657,1658,1661],{"className":1654},[1628],[1630,1656],{"disabled":443,"type":1632}," ",[33,1659,1660],{},"systemctl status gunicorn"," shows the service is active.",[176,1663,1665,1667],{"className":1664},[1628],[1630,1666],{"disabled":443,"type":1632}," Gunicorn bind target matches the Nginx upstream target.",[176,1669,1671,1657,1673,1676],{"className":1670},[1628],[1630,1672],{"disabled":443,"type":1632},[33,1674,1675],{},"nginx -t"," passes without errors.",[176,1678,1680,1682],{"className":1679},[1628],[1630,1681],{"disabled":443,"type":1632}," Nginx server block uses the correct domain and reverse proxy settings.",[176,1684,1686,1688],{"className":1685},[1628],[1630,1687],{"disabled":443,"type":1632}," Public DNS resolves to the correct server IP.",[176,1690,1692,1694],{"className":1691},[1628],[1630,1693],{"disabled":443,"type":1632}," Static file paths in Nginx match actual filesystem locations.",[176,1696,1698,1700],{"className":1697},[1628],[1630,1699],{"disabled":443,"type":1632}," Media or upload directories exist and have correct permissions.",[176,1702,1704,1706],{"className":1703},[1628],[1630,1705],{"disabled":443,"type":1632}," Environment variables are loaded in production.",[176,1708,1710,1712],{"className":1709},[1628],[1630,1711],{"disabled":443,"type":1632}," Database credentials are correct and reachable from the server.",[176,1714,1716,1718],{"className":1715},[1628],[1630,1717],{"disabled":443,"type":1632}," Migrations have been applied.",[176,1720,1722,1724],{"className":1721},[1628],[1630,1723],{"disabled":443,"type":1632}," HTTPS is configured and the certificate is valid.",[176,1726,1728,1730],{"className":1727},[1628],[1630,1729],{"disabled":443,"type":1632}," Firewall allows ports 80 and 443.",[176,1732,1734,1736],{"className":1733},[1628],[1630,1735],{"disabled":443,"type":1632}," Gunicorn and Nginx are enabled on boot.",[176,1738,1740,1742],{"className":1739},[1628],[1630,1741],{"disabled":443,"type":1632}," Logs are accessible for Nginx and Gunicorn.",[176,1744,1746,1748],{"className":1745},[1628],[1630,1747],{"disabled":443,"type":1632}," External requests to the public domain return the expected response.",[176,1750,1752,1754],{"className":1751},[1628],[1630,1753],{"disabled":443,"type":1632}," Key app flows work after deploy.",[18,1756,1758],{"id":1757},"related-guides","Related Guides",[589,1760,1761,1767,1771,1777],{},[176,1762,1763],{},[999,1764,1766],{"href":1765},"\u002Fdeploy\u002Fdeploy-flask-with-nginx-plus-gunicorn-step-by-step-guide","Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide)",[176,1768,1769],{},[999,1770,1616],{"href":1615},[176,1772,1773],{},[999,1774,1776],{"href":1775},"\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do","Flask Production Checklist (Everything You Must Do)",[176,1778,1779],{},[999,1780,1288],{"href":1287},[18,1782,1784],{"id":1783},"faq","FAQ",[14,1786,1787,1790,1792],{},[179,1788,1789],{},"Q: Is this checklist enough for a first production launch?",[183,1791],{},"\nA: It covers the core deployment path and the most common failure points. Add security, monitoring, backup, and scaling checks for a complete production review.",[14,1794,1795,1798,1800],{},[179,1796,1797],{},"Q: What is the fastest way to verify the stack is healthy?",[183,1799],{},"\nA: Check Gunicorn and Nginx status, validate Nginx config, test the app locally, then test the public HTTPS endpoint.",[14,1802,1803,1806,1808,1809,1812],{},[179,1804,1805],{},"Q: Why does the app work with Gunicorn manually but fail through systemd?",[183,1807],{},"\nA: systemd often runs with a different working directory, user, ",[33,1810,1811],{},"PATH",", and environment variables. Compare the service file with the manual command.",[14,1814,1815,1818,1820],{},[179,1816,1817],{},"Q: Should static files be served by Flask in production?",[183,1819],{},"\nA: No. In most production setups, Nginx should serve static and media files directly.",[14,1822,1823,1826,1828],{},[179,1824,1825],{},"Q: What if the domain works but returns 502?",[183,1827],{},"\nA: Nginx is reachable, but Gunicorn is not responding correctly. Check upstream target, Gunicorn service status, and application startup logs.",[18,1830,1832],{"id":1831},"final-takeaway","Final Takeaway",[14,1834,1835],{},"A Flask deployment is production-ready only when the full chain is validated: app, environment, Gunicorn, systemd, Nginx, database, static or media paths, DNS, and HTTPS.",[14,1837,1838],{},"Use this checklist to catch mismatches early and confirm the deployment works from both the server and the public domain.",[1840,1841,1842],"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 .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}",{"title":31,"searchDepth":56,"depth":56,"links":1844},[1845,1846,1847,1848,1849,1850,1851,1852,1853],{"id":20,"depth":56,"text":21},{"id":157,"depth":56,"text":158},{"id":170,"depth":56,"text":171},{"id":1248,"depth":56,"text":1249},{"id":1344,"depth":56,"text":1345},{"id":1619,"depth":56,"text":1620},{"id":1757,"depth":56,"text":1758},{"id":1783,"depth":56,"text":1784},{"id":1831,"depth":56,"text":1832},"Complete guide on flask deployment checklist for Flask production environments.","md",{"ogTitle":5,"ogDescription":1854,"twitterCard":1857,"robots":1858,"canonical":1859},"summary_large_image","index, follow","https:\u002F\u002Fflask-deployment.com\u002Fchecklist\u002Fflask-deployment-checklist","\u002Fchecklist\u002Fflask-deployment-checklist",{"title":5,"description":1854},"checklist\u002Fflask-deployment-checklist","RuI2OOH5d2aectHAo4dMEp2hf1dIn7G0ee1PThQFh9o",1776805765072]