0

I have the following unit file:

[Unit]
Description=Panel for Systemd Services
After=network.target

[Service]
User=pysd
Group=pysd
PermissionsStartOnly=true
WorkingDirectory=/opt/pysd
ExecStartPre=/bin/mkdir /run/pysd
ExecStartPre=/bin/chown -R pysd:pysd /run/pysd
ExecStart=/usr/local/bin/gunicorn app:app -b 127.0.0.1:8100 --pid /run/pysd/pysd.pid --workers=2
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
ExecStopPost=/bin/rm -rf /run/pysd
PIDFile=/run/pysd/pysd.pid
PrivateTmp=true

[Install]
WantedBy=multi-user.target
Alias=pysd.service

I would like to create an environment variable with ExecStartPre and then incorporating this variable to ExecStart.

To be more specific, I want to create an environment variable GUNICORN_SERVER, before running the ExecStart, and then using this environment variable for the option -b at ExecStart.

I tried something like ExecStartPre=/bin/bash -c 'export GUNICORN_SERVER=127.0.0.1:8100', but no environment variable was created.

How do I achieve this scenario?

  • 1
    Why not just Environment=GUNICORN_SERVER=127.0.0.1:8080? – muru Apr 11 at 4:22
  • I tried this, and it seemed to work fine, but I'm not sure of the results, when using os.environ.get('GUNICORN_SERVER') on my Python application. I have to review here.. Tks anyway, muru. – ivanleoncz Apr 11 at 4:27
  • 3
    You really should read the Gunicorn doco. It documents a systemd service unit, the RuntimeDirectory part of which you have laboriously reinvented. It also documents a systemd socket unit. – JdeBP Apr 11 at 5:46
2

You cannot use ExecStartPre to directly set the environment for other ExecStartPre or ExecStart commands - those are all separate processes. (Indirectly, by saving to a file and reading it or something, sure.)

Systemd has two ways to set the environment: Environment= and EnvironmentFile=. There are examples of both in man 5 systemd.exec. These affect all processes started by the service, including those for ExecStartPre. If these variables don't have to dynamically set, those are a good option:

Environment=GUNICORN_SERVER=127.0.0.1:8080

However, if you need to dynamically set the variables, the manpage says this about EnvironmentFile:

The files listed with this directive will be read shortly before the process is
executed (more specifically, after all processes from a previous unit state
terminated. This means you can generate these files in one unit state, and read it
with this option in the next).

So, one option would be to write it to a file in ExecStartPre, and have systemd read that file as part of EnvironmentFile:

EnvironmentFile=/some/env/file
ExecStartPre=/bin/bash -c 'echo foo=bar > /some/env/file'
ExecStart=/some/command  # sees bar as value of $foo

Another option would be to use a shell in ExecStart:

ExecStart=/bin/sh -c 'export GUNICORN_SERVER=127.0.0.1:8080; exec /usr/local/bin/gunicorn ...'
  • 1
    The systemd people do not like this. unix.stackexchange.com/a/419061/5132 – JdeBP Apr 11 at 5:35
  • @JdeBP interesting, and makes sense. I always found it weird to have an EnvironmentFile jutting into a cascading series of service files. – muru Apr 11 at 5:48
  • 2
    Also note that the systemd people would say to use a socket unit to describe the listening socket, and the Gunicorn doco actually has one. – JdeBP Apr 11 at 5:49
  • Unfortunately writing it to a file in ExecStartPre doesn't seem to work because it actually reads that file before running the ExecStartPre's, one workaround here: stackoverflow.com/a/42841480/32453 – rogerdpack May 18 at 1:31
0

Indeed, the best approach, is to use the --env from Gunicorn, which creates an environment variable on the OS, which you can read and incorporate the data to your Python application:

On your unit file:

ExecStart=/usr/local/bin/gunicorn app:app -b 127.0.0.1:8100 --env GUNICORN_SERVER="127.0.0.1:8100" --pid /run/pysd/pysd.pid --workers=2

On your application (Flask, for example):

import os
from flask import Flask

app = Flask(__name__)

gs = os.getenv('GUNICORN_SERVER')
if gs:
    app.config["SERVER_NAME"] = gs                                              
else:
    app.config["SERVER_NAME"] = "127.0.0.1:8100"

Thanks for your comment, JdeBP. The documentation has a lot of information, indeed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.