Launching a Python Script via Gunicorn + Nginx on a Server Managed by Hestia CP

Andy Wits Blog

Launching a Python Script via Gunicorn + Nginx on a Server Managed by Hestia CP

Ready-made solutions for Hestia CP were not found. Here are some helpful thoughts in these articles: https://github.com/realjumy/hestiacp-python-templates https://help.clouding.io/hc/es/articles/4408217149458-C%C3%B3mo-instalar-Django-y-Gunicorn-en-HestiaCP

Therefore, I did it simply in 2 stages:

Creating a Gunicorn Nginx template for Hestia CP without Apache

In our case, Apache is not used, and Hestia CP panel via interface only offers templates in the php-fpm folder. This means we cannot use sh scripts. There are suitable no-php.* templates.

Let's make copies:

cd /usr/local/hestia/data/templates/web/nginx/php-fpm
cp no-php.tpl proxy_gunicorn.tpl
cp no-php.stpl proxy_gunicorn.stpl

Edit the templates:

nano proxy_gunicorn.tpl
nano proxy_gunicorn.stpl

To proxy Gunicorn through Nginx, we only need to take a couple of config snippets from the official documentation and create templates based on them:

    location / {
      # checks for static file, if not found proxy to app
      try_files $uri @proxy_to_app;
    }
    location @proxy_to_app {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header Host $http_host;
      # we don't want nginx trying to do something clever with
      # redirects, we set the Host: header above already.
      proxy_redirect off;
      proxy_pass http://127.0.0.1:8000;
    }

I changed proxy_pass from the documentation to "http://127.0.0.1:8000" and also commented out some lines in the original templates (in particular, the line with the index parameter and the fragment describing location ~* ^.+.(jpeg|jpg|png|webp|gif|bmp|ico|svg|css|js) ... ). No other changes were necessary.

In the Hestia CP panel settings, simply select our template, and for php-fpm - "no-php".

The result is the following configuration for Hestia CP:

proxy_gunicorn.tpl

#=========================================================================#
# Default Web Domain Template                                             #
# DO NOT MODIFY THIS FILE! CHANGES WILL BE LOST WHEN REBUILDING DOMAINS   #
# https://hestiacp.com/docs/server-administration/web-templates.html      #
#=========================================================================#

server {
        listen      %ip%:%web_port%;
        server_name %domain_idn% %alias_idn%;
        root        %docroot%;
#       index       index.php index.html index.htm;
        access_log  /var/log/nginx/domains/%domain%.log combined;
        access_log  /var/log/nginx/domains/%domain%.bytes bytes;
        error_log   /var/log/nginx/domains/%domain%.error.log error;

        include %home%/%user%/conf/web/%domain%/nginx.forcessl.conf*;

        location ~ /\.(?!well-known\/) {
                deny all;
                return 404;
        }

        location / {
#               location ~* ^.+\.(jpeg|jpg|png|webp|gif|bmp|ico|svg|css|js)$ {
#                       expires max;
#                       fastcgi_hide_header "Set-Cookie";
#               }
                # checks for static file, if not found proxy to app
                try_files $uri @proxy_to_app;
        }

        location @proxy_to_app {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Host $http_host;
                # we don't want nginx trying to do something clever with
                # redirects, we set the Host: header above already.
                proxy_redirect off;
                proxy_pass http://127.0.0.1:8000;
        }
        
        location ~ [^/]\.php(/|$) {
                types { } default_type "text/html";
        }

        location /error/ {
                alias %home%/%user%/web/%domain%/document_errors/;
        }

        location /vstats/ {
                alias   %home%/%user%/web/%domain%/stats/;
                include %home%/%user%/web/%domain%/stats/auth.conf*;
        }

        include /etc/nginx/conf.d/phpmyadmin.inc*;
        include /etc/nginx/conf.d/phppgadmin.inc*;
        include %home%/%user%/conf/web/%domain%/nginx.conf_*;
}

proxy_gunicorn.stpl

#=========================================================================#
# Default Web Domain Template                                             #
# DO NOT MODIFY THIS FILE! CHANGES WILL BE LOST WHEN REBUILDING DOMAINS   #
# https://hestiacp.com/docs/server-administration/web-templates.html      #
#=========================================================================#

server {
        listen      %ip%:%web_ssl_port% ssl;
        server_name %domain_idn% %alias_idn%;
        root        %sdocroot%;
#       index       index.php index.html index.htm;
        access_log  /var/log/nginx/domains/%domain%.log combined;
        access_log  /var/log/nginx/domains/%domain%.bytes bytes;
        error_log   /var/log/nginx/domains/%domain%.error.log error;

        ssl_certificate     %ssl_pem%;
        ssl_certificate_key %ssl_key%;
        ssl_stapling        on;
        ssl_stapling_verify on;

        # TLS 1.3 0-RTT anti-replay
        if ($anti_replay = 307) { return 307 https://$host$request_uri; }
        if ($anti_replay = 425) { return 425; }

        include %home%/%user%/conf/web/%domain%/nginx.hsts.conf*;

        location ~ /\.(?!well-known\/) {
                deny all;
                return 404;
        }

        location / {
#               location ~* ^.+\.(jpeg|jpg|png|webp|gif|bmp|ico|svg|css|js)$ {
#                       expires max;
#                       fastcgi_hide_header "Set-Cookie";
#               }
                # checks for static file, if not found proxy to app
                try_files $uri @proxy_to_app;
        }

        location @proxy_to_app {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Host $http_host;
                # we don't want nginx trying to do something clever with
                # redirects, we set the Host: header above already.
                proxy_redirect off;
                proxy_pass http://127.0.0.1:8000;
        }

        location ~ [^/]\.php(/|$) {
                types { } default_type "text/html";
        }

        location /error/ {
                alias %home%/%user%/web/%domain%/document_errors/;
        }

        location /vstats/ {
                alias   %home%/%user%/web/%domain%/stats/;
                include %home%/%user%/web/%domain%/stats/auth.conf*;
        }

        proxy_hide_header Upgrade;

        include /etc/nginx/conf.d/phpmyadmin.inc*;
        include /etc/nginx/conf.d/phppgadmin.inc*;
        include %home%/%user%/conf/web/%domain%/nginx.ssl.conf_*;
}

Setting up a Python application and running Gunicorn in the console

As root, first install the package to set up venv:

apt install python3.12-venv

Then in the site folder created via Hestia CP, set up a Python venv and test it in the console:

python3 -m venv /home/user/web/example.com/public_html
cd /home/user/web/example.com/public_html/
source bin/activate
pip install dash
pip install gunicorn
pip install python-dateutil
pip install pandas
pip install dash-bootstrap-components
pip install gevent
bin/gunicorn -b 127.0.0.1:8000 app:server

Configuring Supervisor to run Gunicorn on Linux server, Hestia CP + Nginx

Install the package:

apt install supervisor

Configure it:

cd /etc/supervisor/conf.d
echo "[program:example_com]
command=source /home/user/web/example.com/public_html/bin/activate && bin/gunicorn -b 127.0.0.1:8000 app:server
directory=/home/user/web/example.com/public_html/
user=user
autostart=true
autorestart=true
redirect_stderr=true" > example_com.conf

The service is managed by the following commands:

supervisorctl status example_com
supervisorctl start example_com
supervisorctl restart example_com
supervisorctl stop example_com

You can also completely restart the supervisor service:

systemctl restart supervisor
Published on Nov 05 2024 at 10:00 am
Time to read 5 minute(s)
Categories
  • System Administration

Contact me on