How to enable a virtualenv in a systemd service unit?
Solution 1
The virtualenv is "baked into the Python interpreter in the virtualenv". This means you can launch python
or console_scripts
directly in that virtualenv and don't need to activate the virtualenv first or manage PATH
yourself.:
ExecStart={{ venv_home }}/bin/fooservice --serve-in-foreground
or
ExecStart={{ venv_home }}/bin/python {{ venv_home }}/fooservice.py --serve-in-foreground
and remove the EnvironmentFile
entry.
To verify that it is indeed correct you can check sys.path
by running
{{ venv_home }}/bin/python -m site
and comparing the output to
python -m site
Solution 2
While the path for libraries is indeed baked into the python interpreter of the virtualenv, I've had issues with python tools that were using binaries installed in that virtualenv. For instance, my apache airflow service wouldn't work because it couldn't find the gunicorn
binary. To work around this, here's my ExecStart
instruction, with an Environment
instruction (which sets an environment variable for the service alone).
ExecStart={{ virtualenv }}/bin/python {{ virtualenv }}/bin/airflow webserver
Environment="PATH={{ virtualenv }}/bin:{{ ansible_env.PATH }}"
ExecStart
explicitly uses the python interpreter of the virtualenv. I'm also adding a PATH
variable, which adds the binary folder of the virtualenv before the system PATH
. That way, I get the desired python libraries as well as binaries.
Note that I'm using ansible to build this service, ergo the curly braces of jinja2.
Solution 3
In my case I just tried to add environment variables required for Flask, for instance
[Service]
Environment="PATH=/xx/yy/zz/venv/bin"
Environment="FLASK_ENV=development"
Environment="APP_SETTINGS=config.DevelopmentConfig"
I was using virtualenv so /xx/yy/zz/venv/bin
is the path of virtualenv folder.
Solution 4
I'm not using virtualenv but pyenv: here is it just to use the real .pyenv path in the shebang and make sure it is in the PATH
Ex: pyenv activate flask-prod for user mortenb which is running in prod
/home/mortenb/.pyenv/versions/flask-prod/bin/python --version
Python 3.6.2
Then to my flask scripts starting in systemd *.service I add the following shebang:
#!/home/mortenb/.pyenv/versions/flask-prod/bin/python3
guettli
http://thomas-guettler.de/ Working out loud: https://github.com/guettli/wol
Updated on May 19, 2020Comments
-
guettli almost 4 years
I want to "activate" a virtualenv in a systemd service file.
I would like avoid to have a shell process between the systemd process and the python interpreter.
My current solution looks like this:
[Unit] Description=fooservice After=syslog.target network.target [Service] Type=simple User=fooservice WorkingDirectory={{ venv_home }} ExecStart={{ venv_home }}/fooservice --serve-in-foreground Restart=on-abort EnvironmentFile=/etc/sysconfig/fooservice.env [Install] WantedBy=multi-user.target
/etc/sysconfig/fooservice.env
PATH={{ venv_home }}/bin:/usr/local/bin:/usr/bin:/bin PYTHONIOENCODING=utf-8 PYTHONPATH={{ venv_home }}/... VIRTUAL_ENV={{ venv_home }}
But I am having trouble. I get ImportErrors since some enties in sys.path are missing.
-
guettli almost 8 yearsYes, sys.path is correct. Thank you. I checked the difference between os.environ. It seems that VIRTUAL_ENV is not set, if I use
{{ venv_home }}/bin/python
. Can this make trouble? -
Nils Werner almost 8 yearsI don't know for certain but I have never had issues with
VIRTUAL_ENV
not being set. -
stelios about 7 yearsgood point Nils. Btw, fooservice.py doesn't make sense to be inside venv_home directory, I suppose its a typo in the question.
-
Mark Edington about 7 yearsNote the suggested print commands are not compatible with Python 3. If you are using at least python 2.4, you can alternatively just use:
python -m site
to get a nicely formatted output of the sys.path variable along with additional information. -
Nils Werner about 7 yearsNeat, I didn't know about
python -m site
. I have adjusted my answer. -
Shubham Namdeo over 6 yearsWorked like a charm for me. Thanks.
-
Jason R. Coombs over 6 yearsThis approach doesn't work when the python process you're starting expects the virtualenv to be activated. For example, consider
{{ venv_home }}/bin/python -m newrelic.admin run-program python -m myapp
. That will launch python in the venv, invoke New Relic, and then it will fail to launch the app becausepython
is not in the path because the venv is not activated. -
Nils Werner over 6 yearsHave you tried
{{ venv_home }}/bin/python -m newrelic.admin run-program {{ venv_home }}/bin/python -m myapp
? -
brainstorm about 6 yearsAlso appears to fail with python3 when the (
python3 -mvenv
created) venv has third party modules not in the python3 stdlib? See here: gist.github.com/brainstorm/bff8b439072ffadbd95ae901c659a6b5 -
Nils Werner about 6 years@brainstorm I cannot reproduce your issues on OSX: gist.github.com/nils-werner/d98aff2b865dac9375570340ce635844. Maybe it has to with pcgr
-
brainstorm about 6 years@NilsWerner I ended up solving it by spawning a shell, nothing else worked on Ubuntu 17.10: github.com/umccr/pcgr-deploy/blob/master/ansible/files/… ... please ignore the jinja2 templating for ansible, it expands correctly when deployed.
-
ankostis almost 6 yearsFor those wondering if this is ninja2....no, the double curly-braces are just placeholders the OP invented: superuser.com/questions/1209919/…
-
Shankara Narayana over 3 yearswhat exactly does --serve-in-foreground do?
-
Nils Werner over 3 yearsThat's a parameter of the Python server the OP invented.
-
FilBot3 about 3 yearsWhen I use the
{{ venv_home }}
syntax, I get errors that it's not a path. -
pyFiddler almost 3 yearsLiterally came looking for how to set the environment in a service because airflow couldn't find gunicorn. Was NOT disappointed! Thank you! I'm up and running.
-
Manju N almost 3 yearsExactly , I was in need of Airflow automation through Ansible . Thank you so much :)