
{"id":5235,"date":"2025-04-08T12:50:41","date_gmt":"2025-04-08T16:50:41","guid":{"rendered":"https:\/\/ikriv.com\/blog\/?p=5235"},"modified":"2025-04-08T12:50:41","modified_gmt":"2025-04-08T16:50:41","slug":"apache-mod_wsgi-demystifying-wsgiapplicationgroup","status":"publish","type":"post","link":"https:\/\/ikriv.com\/blog\/?p=5235","title":{"rendered":"Apache mod_wsgi: demystifying WSGIApplicationGroup"},"content":{"rendered":"<h1>Summary<\/h1>\n<p><code>WSGIApplicationGroup<\/code> is a name of the Python sub-interpreter. You don&#8217;t need sub-interpreters if you run mod_wsgi in daemon mode (i.e. with dedicated processes). Always set <code>WSGIApplicationGroup<\/code> to <code>%{GLOBAL}<\/code>. If you have multiple applications, use &lt;Location&gt; tag in your site configuration, as follows:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nWSGIDaemonProcess app_one processes=2 threads=15 display-name=%{GROUP}\r\nWSGIScriptAlias \/api\/wsgi\/one \/var\/www\/mysite\/api\/one\/wsgi.py\r\n\r\n&lt;Location \/api\/wsgi\/one&gt;\r\n  WSGIProcessGroup app_one\r\n  WSGIApplicationGroup %{GLOBAL}\r\n&lt;\/Location&gt;\r\n\r\nWSGIDaemonProcess app_two processes=2 threads=15 display-name=%{GROUP}\r\nWSGIScriptAlias \/api\/wsgi\/two \/var\/www\/mysite\/api\/two\/wsgi.py\r\n\r\n&lt;Location \/api\/wsgi\/two&gt;\r\n  WSGIProcessGroup app_two\r\n  WSGIApplicationGroup %{GLOBAL}\r\n&lt;\/Location&gt;\r\n<\/pre>\n<h1>More details<\/h1>\n<p>I am experimenting with running WSGI apps, and my server is Apache. There seems to be a lot of debate how to run Flask apps properly:<\/p>\n<ul>\n<li>Using nginx + gunicorn<\/li>\n<li>Using Apache + mod_wsgi<\/li>\n<li>Using Apache + gunicorn (yes, <a href=\"https:\/\/medium.com\/django-deployment\/how-to-setup-apache-with-gunicorn-6616986f1702\">it&#8217;s possible<\/a>)<\/li>\n<\/ul>\n<p>Since I already have Apache, using <code>mod_wsgi<\/code> seems like a natural choice, but there are some caveats, especially if you run multiple applications.<\/p>\n<p>To the best of my understanding, the following rules apply.<\/p>\n<p>You define your application(s) using <code>WSGIScriptAlias<\/code> directive in the site config file. This is relatively straightforward.<\/p>\n<p>You define one or more pools of WSGI &#8220;daemons&#8221; using <code>WSGIDaemonProcess<\/code> directive.<\/p>\n<p>You tell Apache which application runs in which daemon pool via <code>WSGIProcessGroup<\/code> directive. You must use the<br \/>\n&lt;Location&gt; tag to differentiate between different applications, as shown in the example above.<\/p>\n<p>You then tell Apache which Python sub-interpreter you want to use for your processing. When you are running inside a dedicated process,<br \/>\nthere is no point using a sub-interpreter, so the specification is <code>WSGIApplicationGroup %{GLOBAL}<\/code>. In particular, numpy doesn&#8217;t<br \/>\nlike sub-interpreters and prints a warning like this:<\/p>\n<pre>\r\nUserWarning: NumPy was imported from a Python sub-interpreter but NumPy does not properly support sub-interpreters.\r\nThis will likely work for most users but might cause hard to track down issues or subtle bugs.\r\nA common user of the rare sub-interpreter feature is wsgi which also allows single-interpreter mode.\r\nImprovements in the case of bugs are welcome, but is not on the NumPy roadmap, \r\nand full support may require significant effort to achieve.\r\n<\/pre>\n<p>In general, <code>mod_wsgi<\/code> works well, but its defaults are targeted towards the old use case, when the requests<br \/>\nwere handled in the main Apache process, and each application used its own Python sub-interpreter. You must explicitly<br \/>\nspecify the process pool and the &#8220;global&#8221; sub-interpreter for each application. This makes the configuration somewhat<br \/>\nverbose, but once you know what you&#8217;re doing it is not a big deal.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary WSGIApplicationGroup is a name of the Python sub-interpreter. You don&#8217;t need sub-interpreters if you run mod_wsgi in daemon mode (i.e. with dedicated processes). Always set WSGIApplicationGroup to %{GLOBAL}. If <a href=\"https:\/\/ikriv.com\/blog\/?p=5235\" class=\"more-link\">[&hellip;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"Layout":"","footnotes":""},"categories":[4,15],"tags":[],"class_list":["entry","author-ikriv","post-5235","post","type-post","status-publish","format-standard","category-hack","category-webdev"],"_links":{"self":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5235","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5235"}],"version-history":[{"count":3,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5235\/revisions"}],"predecessor-version":[{"id":5238,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5235\/revisions\/5238"}],"wp:attachment":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5235"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5235"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5235"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}