[{"data":1,"prerenderedAt":1498},["ShallowReactive",2],{"\u002Freference\u002Fflask-production-folder-structure-reference":3},{"id":4,"title":5,"body":6,"description":1488,"extension":1489,"meta":1490,"navigation":594,"path":1494,"seo":1495,"stem":1496,"__hash__":1497},"content\u002Freference\u002Fflask-production-folder-structure-reference.md","Flask Production Folder Structure Reference",{"type":7,"value":8,"toc":1463},"minimark",[9,13,17,22,30,123,126,134,140,144,147,151,890,894,976,980,983,988,1030,1033,1044,1048,1081,1083,1100,1104,1142,1144,1167,1171,1206,1208,1219,1223,1250,1252,1273,1277,1289,1291,1296,1300,1372,1376,1397,1401,1405,1410,1414,1417,1421,1427,1431,1434,1438,1441,1445,1452,1456,1459],[10,11,5],"h1",{"id":12},"flask-production-folder-structure-reference",[14,15,16],"p",{},"If you're trying to organize a Flask app for production or fix a messy deployment layout, this guide shows you a proven folder structure step-by-step. The outcome is a predictable layout that works cleanly with Gunicorn, Nginx, systemd, static files, media uploads, logs, and repeatable deploys.",[18,19,21],"h2",{"id":20},"quick-fix-quick-setup","Quick Fix \u002F Quick Setup",[14,23,24,25,29],{},"Create a production-safe base layout under ",[26,27,28],"code",{},"\u002Fsrv\u002Fmyapp",":",[31,32,37],"pre",{"className":33,"code":34,"language":35,"meta":36,"style":36},"language-bash shiki shiki-themes github-light github-dark","sudo mkdir -p \u002Fsrv\u002Fmyapp\u002F{app,run,shared\u002F{static,media,logs},releases}\nsudo python3 -m venv \u002Fsrv\u002Fmyapp\u002Fvenv\nsudo chown -R www-data:www-data \u002Fsrv\u002Fmyapp\nfind \u002Fsrv\u002Fmyapp -type d -exec chmod 755 {} \\;\n","bash","",[26,38,39,59,76,93],{"__ignoreMap":36},[40,41,44,48,52,56],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sScJk","sudo",[40,49,51],{"class":50},"sZZnC"," mkdir",[40,53,55],{"class":54},"sj4cs"," -p",[40,57,58],{"class":50}," \u002Fsrv\u002Fmyapp\u002F{app,run,shared\u002F{static,media,logs},releases}\n",[40,60,62,64,67,70,73],{"class":42,"line":61},2,[40,63,47],{"class":46},[40,65,66],{"class":50}," python3",[40,68,69],{"class":54}," -m",[40,71,72],{"class":50}," venv",[40,74,75],{"class":50}," \u002Fsrv\u002Fmyapp\u002Fvenv\n",[40,77,79,81,84,87,90],{"class":42,"line":78},3,[40,80,47],{"class":46},[40,82,83],{"class":50}," chown",[40,85,86],{"class":54}," -R",[40,88,89],{"class":50}," www-data:www-data",[40,91,92],{"class":50}," \u002Fsrv\u002Fmyapp\n",[40,94,96,99,102,105,108,111,114,117,120],{"class":42,"line":95},4,[40,97,98],{"class":46},"find",[40,100,101],{"class":50}," \u002Fsrv\u002Fmyapp",[40,103,104],{"class":54}," -type",[40,106,107],{"class":50}," d",[40,109,110],{"class":54}," -exec",[40,112,113],{"class":50}," chmod",[40,115,116],{"class":54}," 755",[40,118,119],{"class":50}," {}",[40,121,122],{"class":54}," \\;\n",[14,124,125],{},"Reference tree:",[31,127,132],{"className":128,"code":130,"language":131,"meta":36},[129],"language-text","\u002Fsrv\u002Fmyapp\u002F\n├── app\u002F                 # current application code or symlink to active release\n├── releases\u002F            # timestamped releases for rollback\n├── shared\u002F\n│   ├── static\u002F          # collected static assets served by Nginx\n│   ├── media\u002F           # user uploads\n│   └── logs\u002F            # app and gunicorn logs if using file logging\n├── run\u002F                 # gunicorn socket or pid files\n└── venv\u002F                # Python virtual environment\n","text",[26,133,130],{"__ignoreMap":36},[14,135,136,137,139],{},"Use ",[26,138,28],{}," as the base path, keep code separate from writable data, and do not store uploads, sockets, or logs inside the app package directory.",[18,141,143],{"id":142},"whats-happening","What’s Happening",[14,145,146],{},"Production Flask deployments fail when code, writable data, sockets, and logs are mixed in one directory. Nginx, Gunicorn, and systemd require predictable paths and correct permissions. A stable layout prevents broken static paths, missing uploads, failed sockets, and destructive deploys.",[18,148,150],{"id":149},"step-by-step-guide","Step-by-Step Guide",[152,153,154,191,226,270,300,344,377,402,433,468,500,560,700,826,852,877,888],"ol",{},[155,156,157,161,164,165,167,168,171,172,175,176],"li",{},[158,159,160],"strong",{},"Create one base directory",[162,163],"br",{},"Use a dedicated service path such as ",[26,166,28],{}," instead of ",[26,169,170],{},"\u002Froot",", ",[26,173,174],{},"\u002Fhome",", or an arbitrary working directory.",[31,177,179],{"className":33,"code":178,"language":35,"meta":36,"style":36},"sudo mkdir -p \u002Fsrv\u002Fmyapp\n",[26,180,181],{"__ignoreMap":36},[40,182,183,185,187,189],{"class":42,"line":43},[40,184,47],{"class":46},[40,186,51],{"class":50},[40,188,55],{"class":54},[40,190,92],{"class":50},[155,192,193,196,198,199],{},[158,194,195],{},"Create the standard top-level directories",[162,197],{},"Create directories for active code, releases, shared writable data, runtime files, and the virtualenv.",[31,200,202],{"className":33,"code":201,"language":35,"meta":36,"style":36},"sudo mkdir -p \u002Fsrv\u002Fmyapp\u002F{app,run,shared\u002F{static,media,logs},releases}\nsudo python3 -m venv \u002Fsrv\u002Fmyapp\u002Fvenv\n",[26,203,204,214],{"__ignoreMap":36},[40,205,206,208,210,212],{"class":42,"line":43},[40,207,47],{"class":46},[40,209,51],{"class":50},[40,211,55],{"class":54},[40,213,58],{"class":50},[40,215,216,218,220,222,224],{"class":42,"line":61},[40,217,47],{"class":46},[40,219,66],{"class":50},[40,221,69],{"class":54},[40,223,72],{"class":50},[40,225,75],{"class":50},[155,227,228,231,233,234,237,238],{},[158,229,230],{},"Use a release-safe layout",[162,232],{},"Either place code directly in ",[26,235,236],{},"\u002Fsrv\u002Fmyapp\u002Fapp"," or use release directories with an active symlink.",[31,239,241],{"className":33,"code":240,"language":35,"meta":36,"style":36},"sudo mkdir -p \u002Fsrv\u002Fmyapp\u002Freleases\u002F2026-04-21-120000\nsudo ln -sfn \u002Fsrv\u002Fmyapp\u002Freleases\u002F2026-04-21-120000 \u002Fsrv\u002Fmyapp\u002Fapp\n",[26,242,243,254],{"__ignoreMap":36},[40,244,245,247,249,251],{"class":42,"line":43},[40,246,47],{"class":46},[40,248,51],{"class":50},[40,250,55],{"class":54},[40,252,253],{"class":50}," \u002Fsrv\u002Fmyapp\u002Freleases\u002F2026-04-21-120000\n",[40,255,256,258,261,264,267],{"class":42,"line":61},[40,257,47],{"class":46},[40,259,260],{"class":50}," ln",[40,262,263],{"class":54}," -sfn",[40,265,266],{"class":50}," \u002Fsrv\u002Fmyapp\u002Freleases\u002F2026-04-21-120000",[40,268,269],{"class":50}," \u002Fsrv\u002Fmyapp\u002Fapp\n",[155,271,272,275,277,278,284,286,287],{},[158,273,274],{},"Keep the Flask package inside the app path",[162,276],{},"Example layout:",[31,279,282],{"className":280,"code":281,"language":131,"meta":36},[129],"\u002Fsrv\u002Fmyapp\u002Fapp\u002F\n├── myproject\u002F\n├── wsgi.py\n├── requirements.txt\n└── migrations\u002F\n",[26,283,281],{"__ignoreMap":36},[162,285],{},"Your Gunicorn entrypoint should resolve from this directory, for example:",[31,288,290],{"className":33,"code":289,"language":35,"meta":36,"style":36},"\u002Fsrv\u002Fmyapp\u002Fvenv\u002Fbin\u002Fgunicorn wsgi:app\n",[26,291,292],{"__ignoreMap":36},[40,293,294,297],{"class":42,"line":43},[40,295,296],{"class":46},"\u002Fsrv\u002Fmyapp\u002Fvenv\u002Fbin\u002Fgunicorn",[40,298,299],{"class":50}," wsgi:app\n",[155,301,302,305,307,308],{},[158,303,304],{},"Store environment files outside the codebase",[162,306],{},"Do not commit secrets into the repository or place them inside the Flask package.",[31,309,311],{"className":33,"code":310,"language":35,"meta":36,"style":36},"sudo touch \u002Fsrv\u002Fmyapp\u002Fshared\u002F.env\nsudo chown www-data:www-data \u002Fsrv\u002Fmyapp\u002Fshared\u002F.env\nsudo chmod 640 \u002Fsrv\u002Fmyapp\u002Fshared\u002F.env\n",[26,312,313,323,333],{"__ignoreMap":36},[40,314,315,317,320],{"class":42,"line":43},[40,316,47],{"class":46},[40,318,319],{"class":50}," touch",[40,321,322],{"class":50}," \u002Fsrv\u002Fmyapp\u002Fshared\u002F.env\n",[40,324,325,327,329,331],{"class":42,"line":61},[40,326,47],{"class":46},[40,328,83],{"class":50},[40,330,89],{"class":50},[40,332,322],{"class":50},[40,334,335,337,339,342],{"class":42,"line":78},[40,336,47],{"class":46},[40,338,113],{"class":50},[40,340,341],{"class":54}," 640",[40,343,322],{"class":50},[155,345,346,349,351,352,354,355,374,376],{},[158,347,348],{},"Send static files to a shared path",[162,350],{},"Keep static assets outside the repo so Nginx can serve them consistently across deploys.",[162,353],{},"Example Flask config:",[31,356,360],{"className":357,"code":358,"language":359,"meta":36,"style":36},"language-python shiki shiki-themes github-light github-dark","STATIC_FOLDER = \"\u002Fsrv\u002Fmyapp\u002Fshared\u002Fstatic\"\n","python",[26,361,362],{"__ignoreMap":36},[40,363,364,367,371],{"class":42,"line":43},[40,365,366],{"class":54},"STATIC_FOLDER",[40,368,370],{"class":369},"szBVR"," =",[40,372,373],{"class":50}," \"\u002Fsrv\u002Fmyapp\u002Fshared\u002Fstatic\"\n",[162,375],{},"If you use a build pipeline, output built assets there.",[155,378,379,382,384,385,354,387],{},[158,380,381],{},"Send uploads and media to a shared path",[162,383],{},"User content must survive redeploys.",[162,386],{},[31,388,390],{"className":357,"code":389,"language":359,"meta":36,"style":36},"UPLOAD_FOLDER = \"\u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia\"\n",[26,391,392],{"__ignoreMap":36},[40,393,394,397,399],{"class":42,"line":43},[40,395,396],{"class":54},"UPLOAD_FOLDER",[40,398,370],{"class":369},[40,400,401],{"class":50}," \"\u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia\"\n",[155,403,404,410,412,413,416,417,419,420],{},[158,405,406,407],{},"Store the Gunicorn socket in ",[26,408,409],{},"run\u002F",[162,411],{},"Do not place sockets in the repo or rely on ",[26,414,415],{},"\u002Ftmp",".",[162,418],{},"Example Gunicorn bind path:",[31,421,423],{"className":33,"code":422,"language":35,"meta":36,"style":36},"--bind unix:\u002Fsrv\u002Fmyapp\u002Frun\u002Fgunicorn.sock\n",[26,424,425],{"__ignoreMap":36},[40,426,427,430],{"class":42,"line":43},[40,428,429],{"class":46},"--bind",[40,431,432],{"class":50}," unix:\u002Fsrv\u002Fmyapp\u002Frun\u002Fgunicorn.sock\n",[155,434,435,438,440,441],{},[158,436,437],{},"Use journald or a shared logs directory",[162,439],{},"If file logging is required, keep logs outside release directories.",[31,442,444],{"className":33,"code":443,"language":35,"meta":36,"style":36},"sudo touch \u002Fsrv\u002Fmyapp\u002Fshared\u002Flogs\u002Fgunicorn.log\nsudo chown -R www-data:www-data \u002Fsrv\u002Fmyapp\u002Fshared\u002Flogs\n",[26,445,446,455],{"__ignoreMap":36},[40,447,448,450,452],{"class":42,"line":43},[40,449,47],{"class":46},[40,451,319],{"class":50},[40,453,454],{"class":50}," \u002Fsrv\u002Fmyapp\u002Fshared\u002Flogs\u002Fgunicorn.log\n",[40,456,457,459,461,463,465],{"class":42,"line":61},[40,458,47],{"class":46},[40,460,83],{"class":50},[40,462,86],{"class":54},[40,464,89],{"class":50},[40,466,467],{"class":50}," \u002Fsrv\u002Fmyapp\u002Fshared\u002Flogs\n",[155,469,470,473,475,476,479,480,497,499],{},[158,471,472],{},"Set ownership for the runtime user",[162,474],{},"If Gunicorn runs as ",[26,477,478],{},"www-data",", assign access accordingly.",[31,481,483],{"className":33,"code":482,"language":35,"meta":36,"style":36},"sudo chown -R www-data:www-data \u002Fsrv\u002Fmyapp\n",[26,484,485],{"__ignoreMap":36},[40,486,487,489,491,493,495],{"class":42,"line":43},[40,488,47],{"class":46},[40,490,83],{"class":50},[40,492,86],{"class":54},[40,494,89],{"class":50},[40,496,92],{"class":50},[162,498],{},"If deploys are performed by another user, split deploy ownership from runtime ownership and grant write access only where needed.",[155,501,502,505,507,508],{},[158,503,504],{},"Set safe directory permissions",[162,506],{},"Keep directories traversable and writable only where required.",[31,509,511],{"className":33,"code":510,"language":35,"meta":36,"style":36},"find \u002Fsrv\u002Fmyapp -type d -exec chmod 755 {} \\;\nsudo chmod 640 \u002Fsrv\u002Fmyapp\u002Fshared\u002F.env\nsudo chmod 775 \u002Fsrv\u002Fmyapp\u002Frun \u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia \u002Fsrv\u002Fmyapp\u002Fshared\u002Flogs\n",[26,512,513,533,543],{"__ignoreMap":36},[40,514,515,517,519,521,523,525,527,529,531],{"class":42,"line":43},[40,516,98],{"class":46},[40,518,101],{"class":50},[40,520,104],{"class":54},[40,522,107],{"class":50},[40,524,110],{"class":54},[40,526,113],{"class":50},[40,528,116],{"class":54},[40,530,119],{"class":50},[40,532,122],{"class":54},[40,534,535,537,539,541],{"class":42,"line":61},[40,536,47],{"class":46},[40,538,113],{"class":50},[40,540,341],{"class":54},[40,542,322],{"class":50},[40,544,545,547,549,552,555,558],{"class":42,"line":78},[40,546,47],{"class":46},[40,548,113],{"class":50},[40,550,551],{"class":54}," 775",[40,553,554],{"class":50}," \u002Fsrv\u002Fmyapp\u002Frun",[40,556,557],{"class":50}," \u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia",[40,559,467],{"class":50},[155,561,562,565,567,568,667,669,670],{},[158,563,564],{},"Configure systemd with consistent paths",[162,566],{},"Example service:",[31,569,573],{"className":570,"code":571,"language":572,"meta":36,"style":36},"language-ini shiki shiki-themes github-light github-dark","[Unit]\nDescription=Gunicorn for myapp\nAfter=network.target\n\n[Service]\nUser=www-data\nGroup=www-data\nWorkingDirectory=\u002Fsrv\u002Fmyapp\u002Fapp\nEnvironmentFile=\u002Fsrv\u002Fmyapp\u002Fshared\u002F.env\nExecStart=\u002Fsrv\u002Fmyapp\u002Fvenv\u002Fbin\u002Fgunicorn \\\n  --workers 3 \\\n  --bind unix:\u002Fsrv\u002Fmyapp\u002Frun\u002Fgunicorn.sock \\\n  wsgi:app\n\n[Install]\nWantedBy=multi-user.target\n","ini",[26,574,575,580,585,590,596,602,608,614,620,626,632,638,644,650,655,661],{"__ignoreMap":36},[40,576,577],{"class":42,"line":43},[40,578,579],{},"[Unit]\n",[40,581,582],{"class":42,"line":61},[40,583,584],{},"Description=Gunicorn for myapp\n",[40,586,587],{"class":42,"line":78},[40,588,589],{},"After=network.target\n",[40,591,592],{"class":42,"line":95},[40,593,595],{"emptyLinePlaceholder":594},true,"\n",[40,597,599],{"class":42,"line":598},5,[40,600,601],{},"[Service]\n",[40,603,605],{"class":42,"line":604},6,[40,606,607],{},"User=www-data\n",[40,609,611],{"class":42,"line":610},7,[40,612,613],{},"Group=www-data\n",[40,615,617],{"class":42,"line":616},8,[40,618,619],{},"WorkingDirectory=\u002Fsrv\u002Fmyapp\u002Fapp\n",[40,621,623],{"class":42,"line":622},9,[40,624,625],{},"EnvironmentFile=\u002Fsrv\u002Fmyapp\u002Fshared\u002F.env\n",[40,627,629],{"class":42,"line":628},10,[40,630,631],{},"ExecStart=\u002Fsrv\u002Fmyapp\u002Fvenv\u002Fbin\u002Fgunicorn \\\n",[40,633,635],{"class":42,"line":634},11,[40,636,637],{},"  --workers 3 \\\n",[40,639,641],{"class":42,"line":640},12,[40,642,643],{},"  --bind unix:\u002Fsrv\u002Fmyapp\u002Frun\u002Fgunicorn.sock \\\n",[40,645,647],{"class":42,"line":646},13,[40,648,649],{},"  wsgi:app\n",[40,651,653],{"class":42,"line":652},14,[40,654,595],{"emptyLinePlaceholder":594},[40,656,658],{"class":42,"line":657},15,[40,659,660],{},"[Install]\n",[40,662,664],{"class":42,"line":663},16,[40,665,666],{},"WantedBy=multi-user.target\n",[162,668],{},"Then reload and start:",[31,671,673],{"className":33,"code":672,"language":35,"meta":36,"style":36},"sudo systemctl daemon-reload\nsudo systemctl enable --now myapp\n",[26,674,675,685],{"__ignoreMap":36},[40,676,677,679,682],{"class":42,"line":43},[40,678,47],{"class":46},[40,680,681],{"class":50}," systemctl",[40,683,684],{"class":50}," daemon-reload\n",[40,686,687,689,691,694,697],{"class":42,"line":61},[40,688,47],{"class":46},[40,690,681],{"class":50},[40,692,693],{"class":50}," enable",[40,695,696],{"class":54}," --now",[40,698,699],{"class":50}," myapp\n",[155,701,702,705,707,708,796,798,799],{},[158,703,704],{},"Configure Nginx to use shared static and media paths",[162,706],{},"Example server block snippet:",[31,709,713],{"className":710,"code":711,"language":712,"meta":36,"style":36},"language-nginx shiki shiki-themes github-light github-dark","server {\n    listen 80;\n    server_name example.com;\n\n    location \u002Fstatic\u002F {\n        alias \u002Fsrv\u002Fmyapp\u002Fshared\u002Fstatic\u002F;\n    }\n\n    location \u002Fmedia\u002F {\n        alias \u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia\u002F;\n    }\n\n    location \u002F {\n        include proxy_params;\n        proxy_pass http:\u002F\u002Funix:\u002Fsrv\u002Fmyapp\u002Frun\u002Fgunicorn.sock;\n    }\n}\n","nginx",[26,714,715,720,725,730,734,739,744,749,753,758,763,767,771,776,781,786,790],{"__ignoreMap":36},[40,716,717],{"class":42,"line":43},[40,718,719],{},"server {\n",[40,721,722],{"class":42,"line":61},[40,723,724],{},"    listen 80;\n",[40,726,727],{"class":42,"line":78},[40,728,729],{},"    server_name example.com;\n",[40,731,732],{"class":42,"line":95},[40,733,595],{"emptyLinePlaceholder":594},[40,735,736],{"class":42,"line":598},[40,737,738],{},"    location \u002Fstatic\u002F {\n",[40,740,741],{"class":42,"line":604},[40,742,743],{},"        alias \u002Fsrv\u002Fmyapp\u002Fshared\u002Fstatic\u002F;\n",[40,745,746],{"class":42,"line":610},[40,747,748],{},"    }\n",[40,750,751],{"class":42,"line":616},[40,752,595],{"emptyLinePlaceholder":594},[40,754,755],{"class":42,"line":622},[40,756,757],{},"    location \u002Fmedia\u002F {\n",[40,759,760],{"class":42,"line":628},[40,761,762],{},"        alias \u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia\u002F;\n",[40,764,765],{"class":42,"line":634},[40,766,748],{},[40,768,769],{"class":42,"line":640},[40,770,595],{"emptyLinePlaceholder":594},[40,772,773],{"class":42,"line":646},[40,774,775],{},"    location \u002F {\n",[40,777,778],{"class":42,"line":652},[40,779,780],{},"        include proxy_params;\n",[40,782,783],{"class":42,"line":657},[40,784,785],{},"        proxy_pass http:\u002F\u002Funix:\u002Fsrv\u002Fmyapp\u002Frun\u002Fgunicorn.sock;\n",[40,787,788],{"class":42,"line":663},[40,789,748],{},[40,791,793],{"class":42,"line":792},17,[40,794,795],{},"}\n",[162,797],{},"Validate and reload:",[31,800,802],{"className":33,"code":801,"language":35,"meta":36,"style":36},"sudo nginx -t\nsudo systemctl reload nginx\n",[26,803,804,814],{"__ignoreMap":36},[40,805,806,808,811],{"class":42,"line":43},[40,807,47],{"class":46},[40,809,810],{"class":50}," nginx",[40,812,813],{"class":54}," -t\n",[40,815,816,818,820,823],{"class":42,"line":61},[40,817,47],{"class":46},[40,819,681],{"class":50},[40,821,822],{"class":50}," reload",[40,824,825],{"class":50}," nginx\n",[155,827,828,831,833,834,836,837],{},[158,829,830],{},"Verify the active app symlink if using releases",[162,832],{},"Confirm that ",[26,835,236],{}," points to the expected release.",[31,838,840],{"className":33,"code":839,"language":35,"meta":36,"style":36},"readlink -f \u002Fsrv\u002Fmyapp\u002Fapp\n",[26,841,842],{"__ignoreMap":36},[40,843,844,847,850],{"class":42,"line":43},[40,845,846],{"class":46},"readlink",[40,848,849],{"class":54}," -f",[40,851,269],{"class":50},[155,853,854,857,859,860,171,863,171,866,171,869,872,873,876],{},[158,855,856],{},"Validate that shared data survives redeploys",[162,858],{},"Replace the active release and confirm that ",[26,861,862],{},"shared\u002Fmedia",[26,864,865],{},"shared\u002Fstatic",[26,867,868],{},"shared\u002Flogs",[26,870,871],{},"run",", and ",[26,874,875],{},"venv"," remain untouched.",[155,878,879,882],{},[158,880,881],{},"Use this recommended reference layout",[31,883,886],{"className":884,"code":885,"language":131,"meta":36},[129],"\u002Fsrv\u002Fmyapp\u002F\n├── app\u002F                  -> active codebase or symlink to current release\n├── releases\u002F             -> timestamped deployments for rollback\n├── shared\u002F\n│   ├── static\u002F           -> static files served by Nginx\n│   ├── media\u002F            -> user uploads persisted across deploys\n│   ├── logs\u002F             -> optional file logs\n│   └── .env              -> environment file if used\n├── run\u002F                  -> Gunicorn socket and PID files\n└── venv\u002F                 -> Python virtual environment\n",[26,887,885],{"__ignoreMap":36},[155,889],{},[18,891,893],{"id":892},"common-causes","Common Causes",[895,896,897,906,917,926,939,949,958],"ul",{},[155,898,899,902,903,416],{},[158,900,901],{},"Code and uploads stored in the same directory"," → redeploys overwrite user data → move uploads to ",[26,904,905],{},"\u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia",[155,907,908,913,914,416],{},[158,909,910,911],{},"Gunicorn socket placed inside the repo or ",[26,912,415],{}," → Nginx cannot access it consistently or it disappears on reboot → use ",[26,915,916],{},"\u002Fsrv\u002Fmyapp\u002Frun\u002Fgunicorn.sock",[155,918,919,922,923,416],{},[158,920,921],{},"Static files stored only inside the Flask package"," → Nginx alias does not match deployment paths → serve from ",[26,924,925],{},"\u002Fsrv\u002Fmyapp\u002Fshared\u002Fstatic",[155,927,928,934,935,938],{},[158,929,930,931,933],{},"Using ",[26,932,170],{}," as the deployment base"," → systemd and Nginx hit permission barriers → move the app to ",[26,936,937],{},"\u002Fsrv"," or another service-safe path.",[155,940,941,944,945,948],{},[158,942,943],{},"Environment file committed into the application directory"," → secrets leak and config differs across releases → store secrets in ",[26,946,947],{},"\u002Fsrv\u002Fmyapp\u002Fshared\u002F.env"," or systemd environment config.",[155,950,951,954,955,416],{},[158,952,953],{},"Logs written inside release directories"," → logs disappear after each deploy → write to journald or ",[26,956,957],{},"\u002Fsrv\u002Fmyapp\u002Fshared\u002Flogs",[155,959,960,963,964,967,968,971,972,975],{},[158,961,962],{},"No releases\u002Fshared separation"," → rollback is hard and deploys are destructive → adopt ",[26,965,966],{},"releases"," plus ",[26,969,970],{},"app"," symlink plus ",[26,973,974],{},"shared"," data layout.",[18,977,979],{"id":978},"debugging-section","Debugging Section",[14,981,982],{},"Check structure, ownership, socket visibility, and service path consistency.",[984,985,987],"h3",{"id":986},"inspect-permissions-and-path-traversal","Inspect permissions and path traversal",[31,989,991],{"className":33,"code":990,"language":35,"meta":36,"style":36},"namei -l \u002Fsrv\u002Fmyapp\nnamei -l \u002Fsrv\u002Fmyapp\u002Frun\nnamei -l \u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia\nnamei -l \u002Fsrv\u002Fmyapp\u002Frun\u002Fgunicorn.sock\n",[26,992,993,1003,1012,1021],{"__ignoreMap":36},[40,994,995,998,1001],{"class":42,"line":43},[40,996,997],{"class":46},"namei",[40,999,1000],{"class":54}," -l",[40,1002,92],{"class":50},[40,1004,1005,1007,1009],{"class":42,"line":61},[40,1006,997],{"class":46},[40,1008,1000],{"class":54},[40,1010,1011],{"class":50}," \u002Fsrv\u002Fmyapp\u002Frun\n",[40,1013,1014,1016,1018],{"class":42,"line":78},[40,1015,997],{"class":46},[40,1017,1000],{"class":54},[40,1019,1020],{"class":50}," \u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia\n",[40,1022,1023,1025,1027],{"class":42,"line":95},[40,1024,997],{"class":46},[40,1026,1000],{"class":54},[40,1028,1029],{"class":50}," \u002Fsrv\u002Fmyapp\u002Frun\u002Fgunicorn.sock\n",[14,1031,1032],{},"What to look for:",[895,1034,1035,1038,1041],{},[155,1036,1037],{},"Every parent directory is traversable",[155,1039,1040],{},"Nginx and Gunicorn users can access the socket path",[155,1042,1043],{},"Writable directories are writable by the runtime user",[984,1045,1047],{"id":1046},"list-the-directory-tree-with-modes-and-owners","List the directory tree with modes and owners",[31,1049,1051],{"className":33,"code":1050,"language":35,"meta":36,"style":36},"ls -lah \u002Fsrv\u002Fmyapp\nfind \u002Fsrv\u002Fmyapp -maxdepth 3 -printf '%M %u %g %p\\n'\n",[26,1052,1053,1063],{"__ignoreMap":36},[40,1054,1055,1058,1061],{"class":42,"line":43},[40,1056,1057],{"class":46},"ls",[40,1059,1060],{"class":54}," -lah",[40,1062,92],{"class":50},[40,1064,1065,1067,1069,1072,1075,1078],{"class":42,"line":61},[40,1066,98],{"class":46},[40,1068,101],{"class":50},[40,1070,1071],{"class":54}," -maxdepth",[40,1073,1074],{"class":54}," 3",[40,1076,1077],{"class":54}," -printf",[40,1079,1080],{"class":50}," '%M %u %g %p\\n'\n",[14,1082,1032],{},[895,1084,1085,1095],{},[155,1086,1087,171,1089,1091,1092,1094],{},[26,1088,871],{},[26,1090,862],{},", and optional ",[26,1093,868],{}," have write access for the service user",[155,1096,1097,1099],{},[26,1098,970],{}," points to the expected release if symlinks are used",[984,1101,1103],{"id":1102},"validate-systemd-service-paths","Validate systemd service paths",[31,1105,1107],{"className":33,"code":1106,"language":35,"meta":36,"style":36},"sudo systemctl cat myapp\nsudo journalctl -u myapp -n 100 --no-pager\n",[26,1108,1109,1120],{"__ignoreMap":36},[40,1110,1111,1113,1115,1118],{"class":42,"line":43},[40,1112,47],{"class":46},[40,1114,681],{"class":50},[40,1116,1117],{"class":50}," cat",[40,1119,699],{"class":50},[40,1121,1122,1124,1127,1130,1133,1136,1139],{"class":42,"line":61},[40,1123,47],{"class":46},[40,1125,1126],{"class":50}," journalctl",[40,1128,1129],{"class":54}," -u",[40,1131,1132],{"class":50}," myapp",[40,1134,1135],{"class":54}," -n",[40,1137,1138],{"class":54}," 100",[40,1140,1141],{"class":54}," --no-pager\n",[14,1143,1032],{},[895,1145,1146,1151,1159,1164],{},[155,1147,1148],{},[26,1149,1150],{},"WorkingDirectory=\u002Fsrv\u002Fmyapp\u002Fapp",[155,1152,1153,1156,1157],{},[26,1154,1155],{},"ExecStart"," uses ",[26,1158,296],{},[155,1160,1161,1162],{},"Socket path matches ",[26,1163,916],{},[155,1165,1166],{},"Environment file path is correct",[984,1168,1170],{"id":1169},"check-socket-creation","Check socket creation",[31,1172,1174],{"className":33,"code":1173,"language":35,"meta":36,"style":36},"sudo ls -lah \u002Fsrv\u002Fmyapp\u002Frun\nsudo ss -xl | grep gunicorn\n",[26,1175,1176,1187],{"__ignoreMap":36},[40,1177,1178,1180,1183,1185],{"class":42,"line":43},[40,1179,47],{"class":46},[40,1181,1182],{"class":50}," ls",[40,1184,1060],{"class":54},[40,1186,1011],{"class":50},[40,1188,1189,1191,1194,1197,1200,1203],{"class":42,"line":61},[40,1190,47],{"class":46},[40,1192,1193],{"class":50}," ss",[40,1195,1196],{"class":54}," -xl",[40,1198,1199],{"class":369}," |",[40,1201,1202],{"class":46}," grep",[40,1204,1205],{"class":50}," gunicorn\n",[14,1207,1032],{},[895,1209,1210,1216],{},[155,1211,1212,1215],{},[26,1213,1214],{},"gunicorn.sock"," exists",[155,1217,1218],{},"Gunicorn is actually listening on the Unix socket",[984,1220,1222],{"id":1221},"validate-nginx-path-mappings","Validate Nginx path mappings",[31,1224,1226],{"className":33,"code":1225,"language":35,"meta":36,"style":36},"sudo nginx -t\nsudo tail -n 100 \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\n",[26,1227,1228,1236],{"__ignoreMap":36},[40,1229,1230,1232,1234],{"class":42,"line":43},[40,1231,47],{"class":46},[40,1233,810],{"class":50},[40,1235,813],{"class":54},[40,1237,1238,1240,1243,1245,1247],{"class":42,"line":61},[40,1239,47],{"class":46},[40,1241,1242],{"class":50}," tail",[40,1244,1135],{"class":54},[40,1246,1138],{"class":54},[40,1248,1249],{"class":50}," \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\n",[14,1251,1032],{},[895,1253,1254,1267,1270],{},[155,1255,1256,1259,1260,1263,1264],{},[26,1257,1258],{},"alias"," paths match ",[26,1261,1262],{},"\u002Fsrv\u002Fmyapp\u002Fshared\u002Fstatic\u002F"," and ",[26,1265,1266],{},"\u002Fsrv\u002Fmyapp\u002Fshared\u002Fmedia\u002F",[155,1268,1269],{},"No permission denied errors",[155,1271,1272],{},"No bad socket path references",[984,1274,1276],{"id":1275},"confirm-active-release-target","Confirm active release target",[31,1278,1279],{"className":33,"code":839,"language":35,"meta":36,"style":36},[26,1280,1281],{"__ignoreMap":36},[40,1282,1283,1285,1287],{"class":42,"line":43},[40,1284,846],{"class":46},[40,1286,849],{"class":54},[40,1288,269],{"class":50},[14,1290,1032],{},[895,1292,1293],{},[155,1294,1295],{},"The symlink resolves to the intended release directory",[18,1297,1299],{"id":1298},"checklist","Checklist",[895,1301,1304,1313,1319,1325,1334,1346,1360,1366],{"className":1302},[1303],"contains-task-list",[155,1305,1308,1312],{"className":1306},[1307],"task-list-item",[1309,1310],"input",{"disabled":594,"type":1311},"checkbox"," Application code is separate from writable directories.",[155,1314,1316,1318],{"className":1315},[1307],[1309,1317],{"disabled":594,"type":1311}," Static files are outside the repo and mapped in Nginx.",[155,1320,1322,1324],{"className":1321},[1307],[1309,1323],{"disabled":594,"type":1311}," Uploads\u002Fmedia are outside the repo and survive redeploys.",[155,1326,1328,1330,1331,416],{"className":1327},[1307],[1309,1329],{"disabled":594,"type":1311}," Gunicorn socket path exists under ",[26,1332,1333],{},"\u002Fsrv\u002Fmyapp\u002Frun",[155,1335,1337,1339,1340,1263,1343,1345],{"className":1336},[1307],[1309,1338],{"disabled":594,"type":1311}," systemd ",[26,1341,1342],{},"WorkingDirectory",[26,1344,1155],{}," paths match the folder structure.",[155,1347,1349,1351,1352,171,1354,1091,1357,416],{"className":1348},[1307],[1309,1350],{"disabled":594,"type":1311}," Runtime user has write access only where required: ",[26,1353,871],{},[26,1355,1356],{},"media",[26,1358,1359],{},"logs",[155,1361,1363,1365],{"className":1362},[1307],[1309,1364],{"disabled":594,"type":1311}," Secrets are not stored inside the app package or Git repository.",[155,1367,1369,1371],{"className":1368},[1307],[1309,1370],{"disabled":594,"type":1311}," Release-based deploys can switch the active app without moving shared data.",[18,1373,1375],{"id":1374},"related-guides","Related Guides",[895,1377,1378,1385,1391],{},[155,1379,1380],{},[1381,1382,1384],"a",{"href":1383},"\u002Fdeploy\u002Fdeploy-flask-with-nginx-plus-gunicorn-step-by-step-guide","Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide)",[155,1386,1387],{},[1381,1388,1390],{"href":1389},"\u002Fdeploy\u002Fflask-static-and-media-files-production-setup","Flask Static and Media Files Production Setup",[155,1392,1393],{},[1381,1394,1396],{"href":1395},"\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do","Flask Production Checklist (Everything You Must Do)",[18,1398,1400],{"id":1399},"faq","FAQ",[984,1402,1404],{"id":1403},"where-should-a-flask-app-live-in-production","Where should a Flask app live in production?",[14,1406,164,1407,1409],{},[26,1408,28],{}," with separate directories for code, shared data, runtime files, and the virtualenv.",[984,1411,1413],{"id":1412},"should-uploads-be-stored-inside-the-flask-project-folder","Should uploads be stored inside the Flask project folder?",[14,1415,1416],{},"No. Store uploads in a shared media directory outside the codebase so redeploys do not remove them.",[984,1418,1420],{"id":1419},"where-should-the-gunicorn-socket-go","Where should the Gunicorn socket go?",[14,1422,1423,1424,1426],{},"Place it in a runtime directory such as ",[26,1425,916],{}," with permissions that allow both Gunicorn and Nginx to access it.",[984,1428,1430],{"id":1429},"should-logs-be-stored-in-the-app-directory","Should logs be stored in the app directory?",[14,1432,1433],{},"No. Use journald or a shared logs directory outside release paths.",[984,1435,1437],{"id":1436},"do-i-need-a-releases-directory-for-small-apps","Do I need a releases directory for small apps?",[14,1439,1440],{},"Not strictly, but it makes rollback and atomic deploys much safer.",[984,1442,1444],{"id":1443},"can-i-keep-the-virtualenv-inside-the-project-repo","Can I keep the virtualenv inside the project repo?",[14,1446,1447,1448,1451],{},"It is better to keep it at ",[26,1449,1450],{},"\u002Fsrv\u002Fmyapp\u002Fvenv"," so code releases stay clean and replaceable.",[18,1453,1455],{"id":1454},"final-takeaway","Final Takeaway",[14,1457,1458],{},"A production-ready Flask folder structure separates code, shared data, runtime files, and environment configuration. That separation makes Nginx, Gunicorn, and systemd more predictable, keeps deploys replaceable, and prevents data loss during updates.",[1460,1461,1462],"style",{},"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 .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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":36,"searchDepth":61,"depth":61,"links":1464},[1465,1466,1467,1468,1469,1477,1478,1479,1487],{"id":20,"depth":61,"text":21},{"id":142,"depth":61,"text":143},{"id":149,"depth":61,"text":150},{"id":892,"depth":61,"text":893},{"id":978,"depth":61,"text":979,"children":1470},[1471,1472,1473,1474,1475,1476],{"id":986,"depth":78,"text":987},{"id":1046,"depth":78,"text":1047},{"id":1102,"depth":78,"text":1103},{"id":1169,"depth":78,"text":1170},{"id":1221,"depth":78,"text":1222},{"id":1275,"depth":78,"text":1276},{"id":1298,"depth":61,"text":1299},{"id":1374,"depth":61,"text":1375},{"id":1399,"depth":61,"text":1400,"children":1480},[1481,1482,1483,1484,1485,1486],{"id":1403,"depth":78,"text":1404},{"id":1412,"depth":78,"text":1413},{"id":1419,"depth":78,"text":1420},{"id":1429,"depth":78,"text":1430},{"id":1436,"depth":78,"text":1437},{"id":1443,"depth":78,"text":1444},{"id":1454,"depth":61,"text":1455},"Complete guide on flask production folder structure reference for Flask production environments.","md",{"ogTitle":5,"ogDescription":1488,"twitterCard":1491,"robots":1492,"canonical":1493},"summary_large_image","index, follow","https:\u002F\u002Fflask-deployment.com\u002Freference\u002Fflask-production-folder-structure-reference","\u002Freference\u002Fflask-production-folder-structure-reference",{"title":5,"description":1488},"reference\u002Fflask-production-folder-structure-reference","4DOQ0_ynLuyA5Ghm_U_OpUrI1yEPPs4bwFss3JFZNXQ",1776805765080]