{"id":58,"date":"2026-03-15T18:02:20","date_gmt":"2026-03-15T18:02:20","guid":{"rendered":"https:\/\/blogs.bharatstacks.com\/?p=58"},"modified":"2026-04-08T17:12:56","modified_gmt":"2026-04-08T17:12:56","slug":"how-i-deploy-my-flask-apps-in-production-docker-nginx-gunicorn","status":"publish","type":"post","link":"https:\/\/blogs.bharatstacks.com\/index.php\/2026\/03\/15\/how-i-deploy-my-flask-apps-in-production-docker-nginx-gunicorn\/","title":{"rendered":"How to Deploy Flask Apps in Production"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Picture this: a developer finishes building a Flask app \u2014 clean code, decent UI, everything working perfectly on&nbsp;<code>localhost:5000<\/code>. Friends are waiting to try it. The app gets pushed to a Cloud Server,&nbsp;<code>flask run<\/code>&nbsp;gets typed, and\u2026 it&#8217;s called done.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two days later the app is down. Nobody can figure out why. SSH into the server and the process has just quietly died. No logs. No alerts. Nothing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the moment most developers realize they have no idea what &#8220;production deployment&#8221; actually means. They know Flask. They don&#8217;t know the machinery that&#8217;s supposed to hold it up.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This post is what should have been there from the start \u2014 a plain explanation of the stack, with enough context to understand&nbsp;why each piece exists.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this post we&#8217;ll be covering following topics:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li style=\"border-style:none;border-width:0px;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:0px;border-bottom-right-radius:0px\">Why the Flask dev server is not for production<\/li>\n\n\n\n<li style=\"border-style:none;border-width:0px;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:0px;border-bottom-right-radius:0px\">Why Gunicorn is needed<\/li>\n\n\n\n<li style=\"border-style:none;border-width:0px;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:0px;border-bottom-right-radius:0px\">Why Nginx sits in front<\/li>\n\n\n\n<li style=\"border-style:none;border-width:0px;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:0px;border-bottom-right-radius:0px\">How Docker simplifies everything<\/li>\n\n\n\n<li style=\"border-style:none;border-width:0px;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:0px;border-bottom-right-radius:0px\">The final setup together<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The Flask dev server is lying to you<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">When&nbsp;<code>flask run<\/code>&nbsp;or&nbsp;<code>python app.py<\/code>&nbsp;is run, Flask starts a tiny built-in server called&nbsp;<strong>Werkzeug<\/strong>. It&#8217;s great for development \u2014 it auto-reloads when files change, shows nice error pages, and prints debug output right in the terminal. Many developers use it for months without thinking twice.<\/p>\n\n\n\n<pre class=\"wp-block-code has-background\" style=\"background-color:#edcdcd\"><code>\"WARNING: This is a development server. Do not use it in a production deployment.\" \u2014 Flask, literally every time you run it.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Most developers have read that warning dozens of times and completely ignored it. Big mistake.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the thing: Werkzeug handles&nbsp;<strong>one request at a time<\/strong>. That&#8217;s fine when only one person is testing the app. But the moment two real users hit the endpoint at the same time, one of them waits. The moment a slow database query locks things up, every other request freezes. The moment the process crashes, the entire app disappears until someone manually SSH&#8217;s back in and restarts it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s not built for traffic. It&#8217;s built for you, alone, at your desk.<\/p>\n\n\n\n<pre class=\"wp-block-code has-fira-code-font-family has-medium-font-size\" style=\"border-top-left-radius:15px;border-top-right-radius:15px;border-bottom-left-radius:15px;border-bottom-right-radius:15px\"><code><strong>The technical reason\n<\/strong>\n<em>Werkzeug is a single-threaded synchronous server. Flask apps are WSGI applications \u2014 they're designed to speak a standard interface (Web Server Gateway Interface) that a proper production server can call. Werkzeug implements that interface just well enough for development.<\/em><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Enter Gunicorn \u2014 the real application server<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Gunicorn (short for &#8220;Green Unicorn&#8221;) is what actually runs Flask code in production. Think of it like this: the Flask app is a function. Gunicorn is the engine that calls that function across multiple parallel workers so the app can handle real traffic.andle real traffic.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Installing it is simple:<\/p>\n\n\n<div \n    class=\"align wp-block-bch-code-highlight\" \n    id=\"bhcCodeHighlight-1\" \n    data-attributes='{&quot;cId&quot;:&quot;27b658f2-3&quot;,&quot;language&quot;:&quot;c&quot;,&quot;lineNumbers&quot;:false,&quot;theme&quot;:&quot;porple&quot;,&quot;headerPart&quot;:true,&quot;headingTitleTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:&quot;10px&quot;,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:&quot;1px&quot;,&quot;fontFamily&quot;:&quot;Aleo&quot;,&quot;textTransform&quot;:&quot;uppercase&quot;},&quot;codeTypo&quot;:{&quot;desktop&quot;:15,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;codeBg&quot;:{&quot;color&quot;:&quot;#292c36&quot;},&quot;align&quot;:&quot;&quot;,&quot;copyBtnOption&quot;:&quot;Text&quot;,&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;height&quot;:{&quot;desktop&quot;:&quot;0px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;headerBg&quot;:{&quot;color&quot;:&quot;#2f446e&quot;},&quot;headerTextColor&quot;:{&quot;color&quot;:&quot;#ffff&quot;},&quot;headerThemeOpt&quot;:&quot;one&quot;,&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[{&quot;hOffset&quot;:&quot;0px&quot;,&quot;vOffset&quot;:&quot;0px&quot;,&quot;blur&quot;:&quot;0px&quot;,&quot;spread&quot;:&quot;0px&quot;,&quot;color&quot;:&quot;rgba(0, 0, 0, 0.16)&quot;,&quot;isInset&quot;:false}],&quot;alignment&quot;:&quot;center&quot;,&quot;copyTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:13,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;},&quot;copyIconSize&quot;:{&quot;desktop&quot;:24,&quot;tablet&quot;:20,&quot;mobile&quot;:18},&quot;copyBtnPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;headerPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;14px&quot;,&quot;left&quot;:&quot;14px&quot;,&quot;bottom&quot;:&quot;14px&quot;,&quot;right&quot;:&quot;14px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;12px&quot;,&quot;left&quot;:&quot;12px&quot;,&quot;bottom&quot;:&quot;12px&quot;,&quot;right&quot;:&quot;12px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;10px&quot;,&quot;left&quot;:&quot;10px&quot;,&quot;bottom&quot;:&quot;10px&quot;,&quot;right&quot;:&quot;10px&quot;}},&quot;copyBtnPosition&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;}},&quot;outputOption&quot;:false,&quot;outputText&quot;:&quot;Hello Word !&quot;,&quot;outputColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#2F446E&quot;},&quot;outputTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:21,&quot;tablet&quot;:17,&quot;mobile&quot;:13},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;outputPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;8px&quot;,&quot;left&quot;:&quot;8px&quot;,&quot;bottom&quot;:&quot;8px&quot;,&quot;right&quot;:&quot;8px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;7px&quot;,&quot;left&quot;:&quot;7px&quot;,&quot;bottom&quot;:&quot;7px&quot;,&quot;right&quot;:&quot;7px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;5px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;outputBorder&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;,&quot;radius&quot;:&quot;7px&quot;}}'\n>\n    <pre id=\"codeHightLight\" style=\"display:none;\">\n        &quot;pip install gunicorn\\n\\n#Run your app with 4 worker processes\\ngunicorn -w 4 -b 0.0.0.0:8000 \\&quot;app:create_app()\\&quot;&quot;    <\/pre>\n<\/div>\n\n\n\t\t \n\n\n<p class=\"wp-block-paragraph\">Those&nbsp;<code>-w 4<\/code>&nbsp;workers are separate OS processes. Each one can handle a request independently. If one worker gets stuck on a slow query, the other three keep going. If one crashes, Gunicorn automatically restarts it. That&#8217;s the difference between a server that limps along and one that actually behaves like a server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The difference becomes obvious the first time someone switches from&nbsp;<code>flask run<\/code>&nbsp;to Gunicorn on a staging server and runs a simple load test. With the dev server, 10 requests at the same time turn into a queue. With Gunicorn and 4 workers, everything comes back at roughly the same time. It sounds obvious in hindsight, but seeing it makes the whole concept click.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"border-top-left-radius:15px;border-top-right-radius:15px;border-bottom-left-radius:15px;border-bottom-right-radius:15px\"><code><strong>How many workers?<\/strong>\n\n<em>A common rule of thumb is (2 \u00d7 number of CPU cores) + 1. On a 2-core server, that's 5 workers. Don't go too high \u2014 each worker is a full Python process with its own memory footprint.<\/em><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Why Nginx sits in front of everything<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This one confused me the longest. If Gunicorn is already serving the app, why add another server in front of it?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The short answer: Gunicorn is good at running Python. Nginx is good at everything else.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nginx is a reverse proxy \u2014 it sits at the front door of your server and decides what to do with each incoming request. It&#8217;s written in C, built to handle thousands of concurrent connections with almost no memory, and has been battle-hardened for decades.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s what Nginx does that Gunicorn was never meant to handle:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Slow clients<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When a client (say, someone on a spotty mobile connection) sends a request slowly, Nginx buffers the entire request before forwarding it to Gunicorn. This means a Gunicorn worker isn&#8217;t sitting idle holding a connection open waiting for a slow upload \u2014 it gets the request all at once and finishes fast. Without this, slow clients can silently exhaust your worker pool.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Static files<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Serving a CSS file or a logo PNG through Python is wasteful. Nginx can serve static files directly from disk at the OS level \u2014 no Python process involved. On a busy app, this alone makes a noticeable difference.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>SSL\/TLS termination<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Nginx handles HTTPS. Your Flask app speaks plain HTTP internally, and Nginx decrypts the incoming traffic before forwarding it. Certificates, renewal, HTTP-to-HTTPS redirects \u2014 all managed at the Nginx layer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Load balancing and routing<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">One Nginx instance can route traffic across multiple Gunicorn processes, across multiple servers, or to completely different backends depending on the URL. It&#8217;s the natural place to add a second app server when you eventually need to scale.<\/p>\n\n\n<div \n    class=\"align wp-block-bch-code-highlight\" \n    id=\"bhcCodeHighlight-2\" \n    data-attributes='{&quot;cId&quot;:&quot;34cfe8d2-1&quot;,&quot;language&quot;:&quot;python&quot;,&quot;lineNumbers&quot;:false,&quot;theme&quot;:&quot;porple&quot;,&quot;headerPart&quot;:true,&quot;codeBg&quot;:{&quot;color&quot;:&quot;#292c36&quot;},&quot;align&quot;:&quot;&quot;,&quot;copyBtnOption&quot;:&quot;Text&quot;,&quot;headingTitleTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;,&quot;textTransform&quot;:&quot;uppercase&quot;},&quot;codeTypo&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;height&quot;:{&quot;desktop&quot;:&quot;0px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;headerBg&quot;:{&quot;color&quot;:&quot;#2f446e&quot;},&quot;headerTextColor&quot;:{&quot;color&quot;:&quot;#ffff&quot;},&quot;headerThemeOpt&quot;:&quot;one&quot;,&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[{&quot;hOffset&quot;:&quot;0px&quot;,&quot;vOffset&quot;:&quot;0px&quot;,&quot;blur&quot;:&quot;0px&quot;,&quot;spread&quot;:&quot;0px&quot;,&quot;color&quot;:&quot;rgba(0, 0, 0, 0.16)&quot;,&quot;isInset&quot;:false}],&quot;alignment&quot;:&quot;center&quot;,&quot;copyTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:13,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;},&quot;copyIconSize&quot;:{&quot;desktop&quot;:24,&quot;tablet&quot;:20,&quot;mobile&quot;:18},&quot;copyBtnPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;headerPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;14px&quot;,&quot;left&quot;:&quot;14px&quot;,&quot;bottom&quot;:&quot;14px&quot;,&quot;right&quot;:&quot;14px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;12px&quot;,&quot;left&quot;:&quot;12px&quot;,&quot;bottom&quot;:&quot;12px&quot;,&quot;right&quot;:&quot;12px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;10px&quot;,&quot;left&quot;:&quot;10px&quot;,&quot;bottom&quot;:&quot;10px&quot;,&quot;right&quot;:&quot;10px&quot;}},&quot;copyBtnPosition&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;}},&quot;outputOption&quot;:false,&quot;outputText&quot;:&quot;Hello Word !&quot;,&quot;outputColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#2F446E&quot;},&quot;outputTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:21,&quot;tablet&quot;:17,&quot;mobile&quot;:13},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;outputPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;8px&quot;,&quot;left&quot;:&quot;8px&quot;,&quot;bottom&quot;:&quot;8px&quot;,&quot;right&quot;:&quot;8px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;7px&quot;,&quot;left&quot;:&quot;7px&quot;,&quot;bottom&quot;:&quot;7px&quot;,&quot;right&quot;:&quot;7px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;5px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;outputBorder&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;,&quot;radius&quot;:&quot;7px&quot;}}'\n>\n    <pre id=\"codeHightLight\" style=\"display:none;\">\n        &quot;server {\\n    listen 80;\\n    server_name yourdomain.com;\\n\\n    #Serve static files directly \\u2014 skip Python entirely\\n    location \\\/static\\\/ {\\n        alias \\\/app\\\/static\\\/;\\n    }\\n\\n    #Forward everything else to Gunicorn\\n    location \\\/ {\\n        proxy_pass         http:\\\/\\\/127.0.0.1:8000;\\n        proxy_set_header   Host $host;\\n        proxy_set_header   X-Real-IP $remote_addr;\\n    }\\n}&quot;    <\/pre>\n<\/div>\n\n\n\t\t \n\n\n<p class=\"wp-block-paragraph\">Once this is set up properly, a few things become immediately clearer. The Flask app no longer needs to care about the client&#8217;s IP (Nginx passes it via a header). Static assets load faster. And the whole architecture makes sense as a diagram rather than a guess.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Docker ties it all together (and kills &#8220;works on my machine&#8221;)<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The old way of doing this was manually installing everything on the server \u2014 Python, pip packages, Nginx config, Gunicorn config. Then one day the app needs to move to a different server. Three hours of reinstalling and debugging later, it&#8217;s clear there has to be a better way.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Docker lets you describe the entire application environment in a file. The exact Python version, the exact dependencies, the exact startup commands \u2014 all locked in and ready to go. If it works on a laptop, it works on the server. Full stop.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the setup that works well for most Flask projects:<\/p>\n\n\n<div \n    class=\"align wp-block-bch-code-highlight\" \n    id=\"bhcCodeHighlight-3\" \n    data-attributes='{&quot;cId&quot;:&quot;0210e9df-b&quot;,&quot;language&quot;:&quot;python&quot;,&quot;lineNumbers&quot;:false,&quot;theme&quot;:&quot;porple&quot;,&quot;headerPart&quot;:true,&quot;codeBg&quot;:{&quot;color&quot;:&quot;#292c36&quot;},&quot;align&quot;:&quot;&quot;,&quot;copyBtnOption&quot;:&quot;Text&quot;,&quot;headingTitleTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;,&quot;textTransform&quot;:&quot;uppercase&quot;},&quot;codeTypo&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;height&quot;:{&quot;desktop&quot;:&quot;0px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;headerBg&quot;:{&quot;color&quot;:&quot;#2f446e&quot;},&quot;headerTextColor&quot;:{&quot;color&quot;:&quot;#ffff&quot;},&quot;headerThemeOpt&quot;:&quot;one&quot;,&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[{&quot;hOffset&quot;:&quot;0px&quot;,&quot;vOffset&quot;:&quot;0px&quot;,&quot;blur&quot;:&quot;0px&quot;,&quot;spread&quot;:&quot;0px&quot;,&quot;color&quot;:&quot;rgba(0, 0, 0, 0.16)&quot;,&quot;isInset&quot;:false}],&quot;alignment&quot;:&quot;center&quot;,&quot;copyTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:13,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;},&quot;copyIconSize&quot;:{&quot;desktop&quot;:24,&quot;tablet&quot;:20,&quot;mobile&quot;:18},&quot;copyBtnPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;headerPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;14px&quot;,&quot;left&quot;:&quot;14px&quot;,&quot;bottom&quot;:&quot;14px&quot;,&quot;right&quot;:&quot;14px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;12px&quot;,&quot;left&quot;:&quot;12px&quot;,&quot;bottom&quot;:&quot;12px&quot;,&quot;right&quot;:&quot;12px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;10px&quot;,&quot;left&quot;:&quot;10px&quot;,&quot;bottom&quot;:&quot;10px&quot;,&quot;right&quot;:&quot;10px&quot;}},&quot;copyBtnPosition&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;}},&quot;outputOption&quot;:false,&quot;outputText&quot;:&quot;Hello Word !&quot;,&quot;outputColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#2F446E&quot;},&quot;outputTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:21,&quot;tablet&quot;:17,&quot;mobile&quot;:13},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;outputPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;8px&quot;,&quot;left&quot;:&quot;8px&quot;,&quot;bottom&quot;:&quot;8px&quot;,&quot;right&quot;:&quot;8px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;7px&quot;,&quot;left&quot;:&quot;7px&quot;,&quot;bottom&quot;:&quot;7px&quot;,&quot;right&quot;:&quot;7px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;5px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;outputBorder&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;,&quot;radius&quot;:&quot;7px&quot;}}'\n>\n    <pre id=\"codeHightLight\" style=\"display:none;\">\n        &quot;#Start from a lean Python image\\nFROM python:3.11-slim\\n\\n#Set working directory inside the container\\nWORKDIR \\\/app\\n\\n#Install dependencies first (Docker layer caching trick)\\nCOPY requirements.txt .\\nRUN pip install --no-cache-dir -r requirements.txt\\n\\n#Copy the rest of the app\\nCOPY . .\\n\\n#Start Gunicorn when the container runs\\nCMD [\\&quot;gunicorn\\&quot;, \\&quot;-w\\&quot;, \\&quot;4\\&quot;, \\&quot;-b\\&quot;, \\&quot;0.0.0.0:8000\\&quot;, \\&quot;app:create_app()\\&quot;]&quot;    <\/pre>\n<\/div>\n\n\n\t\t \n\n\n<p class=\"wp-block-paragraph\">And then the real magic: docker-compose.yml brings Flask\/Gunicorn and Nginx together as two coordinated containers.<\/p>\n\n\n<div \n    class=\"align wp-block-bch-code-highlight\" \n    id=\"bhcCodeHighlight-4\" \n    data-attributes='{&quot;cId&quot;:&quot;ff49c448-0&quot;,&quot;lineNumbers&quot;:false,&quot;theme&quot;:&quot;porple&quot;,&quot;headerPart&quot;:true,&quot;codeBg&quot;:{&quot;color&quot;:&quot;#292c36&quot;},&quot;align&quot;:&quot;&quot;,&quot;language&quot;:&quot;javascript&quot;,&quot;copyBtnOption&quot;:&quot;Text&quot;,&quot;headingTitleTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;,&quot;textTransform&quot;:&quot;uppercase&quot;},&quot;codeTypo&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;height&quot;:{&quot;desktop&quot;:&quot;0px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;headerBg&quot;:{&quot;color&quot;:&quot;#2f446e&quot;},&quot;headerTextColor&quot;:{&quot;color&quot;:&quot;#ffff&quot;},&quot;headerThemeOpt&quot;:&quot;one&quot;,&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[{&quot;hOffset&quot;:&quot;0px&quot;,&quot;vOffset&quot;:&quot;0px&quot;,&quot;blur&quot;:&quot;0px&quot;,&quot;spread&quot;:&quot;0px&quot;,&quot;color&quot;:&quot;rgba(0, 0, 0, 0.16)&quot;,&quot;isInset&quot;:false}],&quot;alignment&quot;:&quot;center&quot;,&quot;copyTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:13,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;},&quot;copyIconSize&quot;:{&quot;desktop&quot;:24,&quot;tablet&quot;:20,&quot;mobile&quot;:18},&quot;copyBtnPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;headerPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;14px&quot;,&quot;left&quot;:&quot;14px&quot;,&quot;bottom&quot;:&quot;14px&quot;,&quot;right&quot;:&quot;14px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;12px&quot;,&quot;left&quot;:&quot;12px&quot;,&quot;bottom&quot;:&quot;12px&quot;,&quot;right&quot;:&quot;12px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;10px&quot;,&quot;left&quot;:&quot;10px&quot;,&quot;bottom&quot;:&quot;10px&quot;,&quot;right&quot;:&quot;10px&quot;}},&quot;copyBtnPosition&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;}},&quot;outputOption&quot;:false,&quot;outputText&quot;:&quot;Hello Word !&quot;,&quot;outputColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#2F446E&quot;},&quot;outputTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:21,&quot;tablet&quot;:17,&quot;mobile&quot;:13},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;outputPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;8px&quot;,&quot;left&quot;:&quot;8px&quot;,&quot;bottom&quot;:&quot;8px&quot;,&quot;right&quot;:&quot;8px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;7px&quot;,&quot;left&quot;:&quot;7px&quot;,&quot;bottom&quot;:&quot;7px&quot;,&quot;right&quot;:&quot;7px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;5px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;outputBorder&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;,&quot;radius&quot;:&quot;7px&quot;}}'\n>\n    <pre id=\"codeHightLight\" style=\"display:none;\">\n        &quot;version: \\&quot;3.9\\&quot;\\n\\nservices:\\n\\n  web:\\n    build: .\\n    restart: unless-stopped\\n    environment:\\n      - FLASK_ENV=production\\n\\n  nginx:\\n    image: nginx:alpine\\n    ports:\\n      - \\&quot;80:80\\&quot;\\n      - \\&quot;443:443\\&quot;\\n    volumes:\\n      - .\\\/nginx.conf:\\\/etc\\\/nginx\\\/conf.d\\\/default.conf\\n      - .\\\/certs:\\\/etc\\\/nginx\\\/certs\\n    depends_on:\\n      - web\\n    restart: unless-stopped&quot;    <\/pre>\n<\/div>\n\n\n\t\t \n\n\n<p class=\"wp-block-paragraph\">Deploying to a fresh server now takes about five minutes. Clone the repo, run&nbsp;<code>docker compose up -d<\/code>, and it&#8217;s live. No manual pip installs, no hunting for which config file is wrong, no &#8220;it works on my machine&#8221; conversations.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The full picture<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Once all three pieces are in place, here&#8217;s how a single web request actually travels through the stack:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"564\" src=\"https:\/\/blogs.bharatstacks.com\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-15-at-23.06.31-1024x564.png\" alt=\"\" class=\"wp-image-79\" style=\"border-top-left-radius:15px;border-top-right-radius:15px;border-bottom-left-radius:15px;border-bottom-right-radius:15px\" srcset=\"https:\/\/blogs.bharatstacks.com\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-15-at-23.06.31-1024x564.png 1024w, https:\/\/blogs.bharatstacks.com\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-15-at-23.06.31-300x165.png 300w, https:\/\/blogs.bharatstacks.com\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-15-at-23.06.31-768x423.png 768w, https:\/\/blogs.bharatstacks.com\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-15-at-23.06.31.png 1292w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Each layer does one thing well. Nginx handles the internet-facing concerns. Gunicorn manages concurrency. Flask handles the business logic. Docker makes sure the whole thing is portable and reproducible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It takes a while to understand why this separation exists. But once it clicks, deployment stops feeling like a chore and starts feeling like part of the craft. Every Flask app deserves infrastructure that won&#8217;t quietly die at 2am on a Tuesday.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>The dev server gets you to localhost. The production stack gets you to the world.<\/em><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If&nbsp;<code>flask run<\/code>&nbsp;is still running on a live server somewhere \u2014 go fix that today. Future you will be grateful.<br><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the stack. Clean, battle-tested, and used by developers shipping real applications every day. But deployment is just one piece of the puzzle \u2014 there&#8217;s a lot more ground to cover. Each post follows the same idea \u2014 just the real explanation of why things work the way they do.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If any of this saved time or cleared up something that was confusing, stick around. The next one might do the same.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Picture this: a developer finishes building a Flask app \u2014 clean code, decent UI, everything working perfectly on&nbsp;localhost:5000. Friends are waiting to try it. The app gets pushed to a Cloud Server,&nbsp;flask run&nbsp;gets typed, and\u2026 it&#8217;s called done. Two days later the app is down. Nobody can figure out why. SSH into the server and [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":171,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16],"tags":[],"class_list":["post-58","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"_links":{"self":[{"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/posts\/58","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/comments?post=58"}],"version-history":[{"count":13,"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/posts\/58\/revisions"}],"predecessor-version":[{"id":192,"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/posts\/58\/revisions\/192"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/media\/171"}],"wp:attachment":[{"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/media?parent=58"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/categories?post=58"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.bharatstacks.com\/index.php\/wp-json\/wp\/v2\/tags?post=58"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}