Deploying Tryton with a Python virtual environment

Introduction

After you have successfully tested Tryton, you want to put Tryton into production. Most of the time you want to have Tryton running on a real server instead of running it on your desktop machine. Also you want to have Tryton to start automatically after a reboot or start.

In this guide we build upon the guide from https://discuss.tryton.org/t/install-tryton-in-a-python-virtual-environment-on-ubuntu so you know how to setup Tryton in a virtual environment.

There are a few things to consider, so before you start:

  • We use Ubuntu as base but RPM flavors like RedHat, RockyLinux etc should work the same.
  • You have a fully running Tryton already with database connection etc.
  • You understand what WSGI, systemd and NginX is.
  • This guide gives you an example how you can run Tryton in a production environment.

Note: We use the following variables:

  • trydbuser as the database user
  • trydbpass as the database password
  • trydb as the database name
  • trypyenv as the name for the virtual environment
  • trysysuser as the name for the system user which will run the Tryton server
  • /srv/tryloc as the place where the venv will be put

You can choose whatever you want and replace the names accordingly.

We use /srv/tryloc so other services can reach this location easily otherwise Apparmor or SELinux will kick in and deny access.

Setup base system

The system consists of several parts which are connected. First there is Gunicorn which basically runs Tryton. Then there is NginX which connects to Gunicorn and sends data from the Tryton clients to Gunicorn which then runs it inside Tryton. NginX is not nessecarrely needed, but it will be a good addition.

Install Gunicorn

The internal webserver (Werkzeug) in Tryton should only be used for development purposes and not for production. In this guide we use Gunicorn as the WSGI server. Gunicorn will be installed in the venv so make sure the venv is activated. As Gunicorn is using workers, we use the gevent workers.

(tryloc) trysysuser@docsrv:~$ pip install gunicorn[gevent]

Create inside your venv a new file called gunicorn.conf.py and put the contents

import os

os.environ['TRYTOND_CONFIG'] = '/srv/tryloc/trytond.conf'

into it and save the file and close it. This file will be used by Gunicorn to get some configuration options for Tryton.

Info: Eventually you can also put more environment variables into this file like the list of databases or the logging configuration. Take a look a the Tryton documentation Configuration file for Tryton — Tryton server

Now let see if you can run Gunicorn from the commandline.

(tryloc) trysysuser@docsrv:~$ gunicorn --workers=5 --worker-class=gevent -b 0.0.0.0:8000 trytond.application:app

This will spin up 5 workers which are bound to port 8000. Connect with the desktop client to the server. Using the ip address should be enough. Also enter trydb as the database name. You should be able to get into Tryton without any error.

Create systemd file for Gunicorn

Now that Gunicorn runs, we can automate the start and stop with a systemd file so that it runs on boot or restart. The files should go into /etc/systemd/system which is only accessible by root so you have to use sudo for that. Create a new file

testadmin@docsrv:~$ sudo nano /etc/systemd/system/trytond.service

and add the following content:

[Unit]
Description=The Trytond ERP server
After=network.target
Requires=network.target

[Service]
Type=notify
User=trysysuser
Group=trysysuser
RuntimeDirectory=trytond
WorkingDirectory=/srv/tryloc
ExecStart=/srv/tryloc/bin/gunicorn \
    --workers=5 \
    --worker-class=gevent \
    --bind=0.0.0.0:8000 \
    'trytond.application:app'
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Info: The ExecStart options can be changed and tuned. Consult the documentation of Gunicorn which options are available.

Warning: When running SELinux you can be bitten by wrong labels on the different files. Check the /var/log/audit/audit.log for more information and use the proper tools to understand what’s going on.

Notify systemd that there was a change in a service file.

testadmin@docsrv:~$ sudo systemctl daemon-reload

Now you can start Tryton with:

testadmin@docsrv:~$ sudo systemctl start trytond

And stop it with:

testadmin@docsrv:~$ sudo systemctl stop trytond

To start Tryton on boottime execute

testadmin@docsrv:~$ sudo systemctl enable trytond

You check the status with:

testadmin@docsrv:~$ sudo systemctl status trytond

You should now be able to connect to Tryton, even after a reboot when you have enabled Tryton to be started at boottime.

You have now a production ready system which you can use.

Putting everything behind a proxy server like NginX

For a more secure environment and help with offloading, caching, loadbalancing etc you can use a proxy server. This server sits between Gunicorn and the client. Configuration of TLS/SSL between a proxy server and the client is easier to setup then doing it through Gunicorn. A proxy server can also manage multiple different Tryton installations through subdomains.

In short, when you are going to open up your production environment to the internet you have to use a proxy server to make your environment more secure and stable (Deploying Gunicorn — Gunicorn 21.2.0 documentation).

Installing NginX

Gunicorn itself strongly suggest to use NginX as proxy server (Deploying Gunicorn — Gunicorn 21.2.0 documentation) so we are also going that route. Install NginX with:

testadmin@docsrv:~$ sudo apt-get install nginx

After installing, check if NginX is running

testadmin@docsrv:~$ sudo systemctl status nginx

If everything is working, you will see a green dot and green active (running). Test if you can reach your server with your webbrowser. You must see a welcome page of some sort depending on the distribution you use. To start NginX on boot or restart we must tell systemd to enable NginX on boottime

testadmin@docsrv:~$ sudo systemctl enable nginx

Connecting Gunicorn and NginX

We have now a running Gunicorn and Nginx, both are reachable from the network which is not needed for Gunicorn. So change /etc/systemd/system/trytond.service and replace --bind=0.0.0.0:8000 with --bind=127.0.0.1:8000 so Gunicorn will only listen on localhost.

After the change you have to tell systemd that you have changed something

testadmin@docsrv:~$ sudo systemctl daemon-reload

After a stop and start from trytond you cannot reach Tryton from the network anymore. However doing

testadmin@docsrv:~$ curl http://localhost:8000

will give you the same answer as before.

Now we have to create a configuration for NginX to tell how it can reach Gunicorn.

testadmin@docsrv:~$ sudo nano /etc/nginx/sites-available/trytond.conf

Add the following content.

upstream trytond_server {
  server 127.0.0.1:8000 fail_timeout=0;
}

server {
  listen 80;
  client_max_body_size 4G;

  # set the correct host(s) for your site
  server_name docsrv.local trytond.docsrv.local;

  keepalive_timeout 5;

  # path for static files
  root /srv/www;

  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://trytond_server;
  }

  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /srv/www;
  }
}

You have to enable the site available by creating a symbolic link to the sites-enabled, which NginX reads from during startup.

testadmin@docsrv:~$ sudo ln -s /etc/nginx/sites-available/trytond.conf /etc/nginx/sites-enabled/

Info: Debian based distributions like Ubuntu are using sites-available and sites-enabled. For other distributions use the conf.d sub directory (you can also directly use that on Ubuntu).

Warning: If there is a default in /etc/nginx/sites-enabled/ remove it.

After enabling the site it’s time to see if the syntax is correct

testadmin@docsrv:~$ sudo nginx -t

It will output something like

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Restart NginX and see if you can connect.

testadmin@docsrv:~$ sudo systemctl restart nginx

Start the desktop client and try to connect. Make sure you have <hostname or ip>:80 added in the Host field because we are now connecting to Tryton through port 80 and not 8000.

Replace connecting between NginX and Gunicorn with a Unix socket

Currently you are using the HTTP port 8000 for the connecting between NginX and Gunicorn. When you have multiple instances of Tryton or want to run another service you will get in trouble very quickly. In that case it’s better to use Unix sockets which are basically ‘files’ on the filesystem and are most of the time in /var/run. Replacing
the HTTP port with a Unix socket is not that complicated. First we create a new systemd file which will create a socket for us.

testadmin@docsrv:~$ sudo nano /etc/systemd/system/trytond.socket

And add the following content

[Unit]
Description=Trytond Gunicorn socket

[Socket]
ListenStream=/run/trytond/trytond.sock
# Our service won't need permissions for the socket, since it
# inherits the file descriptor by socket activation
# only the nginx daemon will need access to the socket
SocketUser=www-data
# Optionally restrict the socket permissions even more.
#SocketMode=XXX

[Install]
WantedBy=sockets.target

Warning: On RPM based systems the user is nginx instead of www-data

Save the file and then edit the /etc/systemd/system/trytond.service and make some changes. The complete file should like this

[Unit]
Description=The Trytond ERP server
After=network.target
Requires=network.target
Requires=trytond.socket

[Service]
Type=notify
User=trysysuser
Group=trysysuser
RuntimeDirectory=trytond
WorkingDirectory=/srv/tryloc
ExecStart=/srv/tryloc/bin/gunicorn \
    --workers=5 \
    --worker-class=gevent \
    'trytond.application:app'
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Because we added and made changes to a systemd file, we need to tell systemd about it.

testadmin@docsrv:~$ sudo systemctl daemon-reload

Start the socket with:

testadmin@docsrv:~$ sudo systemctl start trytond.socket

Stop the socket with:

testadmin@docsrv:~$ sudo systemctl stop trytond.socket

Check the status of the socket with:

testadmin@docsrv:~$ sudo systemctl status trytond.socket

Enable the socket to start on (re)boot:

testadmin@docsrv:~$ sudo systemctl enable trytond.socket

Because the service file is also changed, we need to restart that one as well:

testadmin@docsrv:~$ sudo systemctl restart trytond

What’s left is the change in the NginX configuration. Edit the file /etc/nginx/sites-available/trytond.conf

testadmin@docsrv:~$ sudo nano /etc/nginx/sites-available/trytond.conf and change

upstream trytond_server {
  server 127.0.0.1:8000 fail_timeout=0;
}

To

upstream trytond_server {
  server unix:/var/run/trytond/trytond.sock fail_timeout=0;
}

After the change NginX have to be restarted

testadmin@docsrv:~$ sudo systemctl restart nginx

Test if the connecting to Tryton still works.

Getting the workers and task schedulers to work

Tryton has the ability to execute tasks through workers or a task scheduler. Both (workers and task scheduler) are separate programs which are not running at the moment because we didn’t start them. Both don’t need interaction from the outside so they can just be started and Gunicorn is not needed.

First we create a new systemd service file for the task scheduler:

testadmin@docsrv:~$ sudo nano /etc/systemd/system/trytond-cron.service

And add the following content:

[Unit]
Description=The task scheduler for Trytond server
After=network.target
Requires=network.target

[Service]
User=trysysuser
Group=trysysuser
WorkingDirectory=/srv/tryloc
ExecStart=/srv/tryloc/bin/trytond-cron -c /srv/tryloc/trytond.conf -d <your-database>

[Install]
WantedBy=multi-user.target

We have to do the same for the worker:

testadmin@docsrv:~$ sudo nano /etc/systemd/system/trytond-worker.service

And add the following content:

[Unit]
Description=The workers for Trytond server
After=network.target
Requires=network.target

[Service]
User=trysysuser
Group=trysysuser
WorkingDirectory=/srv/tryloc
ExecStart=/srv/tryloc/bin/trytond-worker -c /srv/tryloc/trytond.conf -d <your-database>

[Install]
WantedBy=multi-user.target

Warning: To make the workers do work, you have to enable them in the trytond.conf. See Configuration file for Tryton — Tryton server

Edit the trytond.conf to enable the workers. Make sure you are the right user.

trysysuser@docsrv:~$ nano /srv/tryloc/trytond.conf

And add or change

[queue]
worker = True

Save and close the file.

Again we go through systemd train:

testadmin@docsrv:~$ sudo systemctl daemon-reload

Because we changed the trytond.conf file we have to restart the Trytond service.

testadmin@docsrv:~$ sudo systemctl restart trytond
testadmin@docsrv:~$ sudo systemctl restart trytond.socket

Start the workers and task scheduler with:

testadmin@docsrv:~$ sudo systemctl start trytond-worker
testadmin@docsrv:~$ sudo systemctl start trytond-cron

Stop them with:

testadmin@docsrv:~$ sudo systemctl stop trytond-worker
testadmin@docsrv:~$ sudo systemctl stop trytond-cron

Check their status with:

testadmin@docsrv:~$ sudo systemctl status trytond-worker
testadmin@docsrv:~$ sudo systemctl status trytond-cron

Enable them to start on (re)boot:

testadmin@docsrv:~$ sudo systemctl enable trytond-worker
testadmin@docsrv:~$ sudo systemctl enable trytond-cron

Securing the connection between client and NginX with SSL/TLS

Adding SSL/TLS certificates to NginX is quite straight forward and many tutorials exists on the internet. Therefore we are not going to dig into this because others do a way better job on that then I can. It also depends on the type of certificate you are going to use. Is it Let’s Encrypt or from an other trusted party etc. So please search the internet how to enable SSL/TLS with NginX.

However I want to point you to Client Usage — Tryton desktop client and read carefully the Warning and Note.

Also keep your firewall as closed as possible, only open ports which are needed to be open to the public. Also look for Apparmor or SELinux to make your environment even more secure.

With this all you have a secure production environment.

Running Gunicorn as another user

Instead of running Gunicor as trysysuser you can also use another user to run Gunicorn. This makes the system a bit safer because we can give that user only read and execute permissions of the different files.

First we need to create a new user called trygun which cannot login because it has no shell. Alongside the user we also create a group with the same name. Choose whatever username you like

testadmin@docsrv:~$ sudo adduser --system --no-create-home --group --shell /usr/sbin/nologin trygun

We can now use that username and group in our systemd files. You have to edit:

  • /etc/systemd/system/trytond.service
  • /etc/systemd/system/trytond-cron.service
  • /etc/systemd/system/trytond-worker.service

And replace:

  • User=trysysuser with User=trygun
  • Group=trysysuser with Group=trygun

Problem is that trygun doesn’t have access permissions into /srv/tryloc because it’s the home of the trysysuser. We must give trygun access to that directory which we are going to do with setfacl. Most of the time this package is installed but use the following command will make sure it exists on your system.

testadmin@docsrv:~$ sudo apt-get install acl

With everything in place we can lock down the /srv/tryloc directory to only be accessed by trysysuser.

testadmin@docsrv:~$ sudo chmod 0700 /srv/tryloc

With setfacl we add read and execute permissions to /srv/tryloc specifically for trygun.

testadmin@docsrv:~$ sudo setfacl -m u:trygun:r-X /srv/tryloc/

Info: Mind the capital X.

Eventually you can byte-compile the Python code inside your venv so it’s not needed to that on runtime. Run as trysysuser with an active venv

(tryloc) trysysuser@docsrv:~$ python -c "import compileall; compileall.compile_dir('lib/', maxlevels=10, force=True, optimize=1)"

Extra steps (Expert mode!)

When you have multiple Tryton instances running for production and testing etc. you have to create multiple systemd service files for each instance. This can be a bit tedious and it’s a good candidate for using systemd unit template files. Read more about them here systemd.unit and systemd.unit

The idea here is that we are going to use the instance name as the variable which connects everything together.

Warning: When going further you probably need to start over with new venv's and databases with the right names. You cannot just rename them.

To set this up:

  • The instance name can be anything, in this example it will be trytonrun1
  • The instances will all live in /srv/tryloc
  • The socket files will be in /var/run/trytond
  • The name of the database must be the same as instance, in this case it will be trytonrun1
  • All the instances are run as the same user trysysuser
  • For NginX you still have to add separate files for each instance. To keep things lined up use the instance name as the filename, in this case trytonrun1.conf
  • The instances can be reached under their own subdomain <instance>.<yourdomain>.<tld>

First make sure you have a Tryton system running with the right database name and in a venv in the right location.

The filename of the systemd files are in the form trytond@.service mention the @ sign which indicates it’s a template.

We start off with the trytond@.socket file.

testadmin@docsrv:~$ sudo nano /etc/systemd/system/trytond@.socket

And add the following content

[Unit]
Description=Gunicorn sockets for different Tryton instances

[Socket]
ListenStream=/run/trytond/%i.sock
# Our service won't need permissions for the socket, since it
# inherits the file descriptor by socket activation
# only the nginx daemon will need access to the socket
SocketUser=www-data
# Optionally restrict the socket permissions even more.
#SocketMode=XXX

[Install]
WantedBy=sockets.target

Next we create the file trytond@.service.

testadmin@docsrv:~$ sudo nano /etc/systemd/system/trytond@.service

And add the following content

[Unit]
Description=Run several instances of the Trytond server
After=network.target
Requires=network.target
Requires=trytond@%i.socket

[Service]
Type=notify
User=trysysuser
Group=trysysuser
RuntimeDirectory=trytond
WorkingDirectory=/srv/tryloc/%i
ExecStart=/srv/tryloc/%i/bin/gunicorn \
    --workers=5 \
    --worker-class=gevent \
    'trytond.application:app'
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true

[Install]
WantedBy=multi-user.target

We also create a new file for the task scheduler:

testadmin@docsrv:~$ sudo nano /etc/systemd/system/trytond-cron@.service

And add the following content:

[Unit]
Description=The task scheduler for instances of the Trytond server
After=network.target
Requires=network.target

[Service]
User=trysysuser
Group=trysysuser
WorkingDirectory=/srv/tryloc/%i
ExecStart=/srv/tryloc/%i/bin/trytond-cron -c /srv/tryloc/trytond.conf -d %i

[Install]
WantedBy=multi-user.target

And for the worker as well.

testadmin@docsrv:~$ sudo nano /etc/systemd/system/trytond-worker@.service

With following content:

[Unit]
Description=The workers for instances of the Trytond server
After=network.target
Requires=network.target

[Service]
User=trysysuser
Group=trysysuser
WorkingDirectory=/srv/tryloc/%i
ExecStart=/srv/tryloc/%i/bin/trytond-worker -c /srv/tryloc/trytond.conf -d %i

[Install]
WantedBy=multi-user.target

As you have (hopefully) noted we use the %i as the variable. In this example it will be replaced with trytonrun1 when the files are run by systemd.

Disable all the systemd files created and enabled in the sections above. Eventually you can remove the other files without the @ symbol. After you have done that, systemd should be updated with the changes.

testadmin@docsrv:~$ sudo systemctl daemon-reload

You can now start the trytonrun1 instance of Tryton with:

testadmin@docsrv:~$ sudo systemctl start trytond@trytonrun1.socket
testadmin@docsrv:~$ sudo systemctl start trytond@trytonrun1
testadmin@docsrv:~$ sudo systemctl start trytond-worker@trytonrun1
testadmin@docsrv:~$ sudo systemctl start trytond-cron@trytonrun1

Replace start with stop or enable to stop and start at boottime respectively.

Last thing left is to change the NginX configuration to meet the new socket name and create difference between the different instances.

Rename the NginX configuration file trytond.conf to trytonrun1.conf to keep things in line.

testadmin@docsrv:~$ sudo mv /etc/nginx/sites-available/trytond.conf /etc/nginx/sites-available/trytonrun1.conf

Then edit the file and change the socket name and server_name. Your file should look something like

upstream trytonrun1 {
  server unix:/var/run/trytond/trytonrun1.sock fail_timeout=0;
}

server {
  listen 80;
  client_max_body_size 4G;

  # set the correct host(s) for your site
  server_name trytonrun1.docsrv.local;

  keepalive_timeout 5;

  # path for static files
  root /srv/www/trytonrun1;

  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://trytonrun1;
  }

  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /srv/www/trytonrun1;
  }
}

Remove the link in /etc/nginx/sites-enabled

testadmin@docsrv:~$ sudo rm /etc/nginx/sites-enabled/trytond.conf

And create a new link to the renamed file

testadmin@docsrv:~$ sudo ln -s /etc/nginx/sites-available/trytonrun1.conf /etc/nginx/sites-enabled/

Test if the syntax of the file is still correct

testadmin@docsrv:~$ sudo nginx -t

it will output something like

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Restart NginX and see if you can connect.

testadmin@docsrv:~$ sudo systemctl restart nginx

If you cannot connect, try a reboot of the system becasue a lot has changed. After all this, you can do a cleanup an make more adjustments so it fits better your needs.

6 Likes

Thank you @edbo for a fantastic tutorial! I have been trying to understand a “production-grade” stack for some time. Brilliant! I will need to make some adaptations for FreeBSD, but the heavy lifting is done! :smiley:

2 Likes

First, thanks for the kind words!

Do you mind to write down your changes? We then can create similar base installation and deployment documents for FreeBSD.

1 Like

Absolutely (as soon as I create some periods and finish a round-trip sales process… :wink: )

It is usually not a good idea to have the source code writable by the user running the service. This is why deployment using the Docker image or distribution packages are preferred.

isn’t it needed to write pre-compiled files of python code at execution time ?

No you can pre-compile them before execution. It is what is done in the docker image and I guess package manager does it also (at least Gentoo).

Otherwise if Python can not write them, it will compile but not store them as cache for the next restart.

Thank you for doing all that work, Eddy. For my, VENVs have proved to be THE ONLY WAY find an access to Tryton at all, and valued Mr.@edbo guided me all the way through those days. Thank you so much. With the docker method, I would have had to give up. I could not master it, although I had some (semi)professional help. Pity that high-level experts refuse to accept that simple truth: Not everybody can (or wants to) master docker. I could not.

Just to have it mentioned:
I baked all of @edbo’s advice into a script. It asks a lot of questions to customize your install and can finish a VENV setup in ~10’, including nginx and certbot. You have the flexibility to add core or your own modules during the install process.
Last achievement: I integrated 2 options to download preconfigured database templates with essential entries (business year, sequences, sample company, sample products etc) to give users an idea what Tryton is all about.

Forgive me that code quality most likely is a nightmare - this is my first coding project EVER. So certainly much room for improvement in any directions. Nevertheless, I find it useful to have a “quick Tryton” for experiments - and for productive use as well.

In case somebody is interested, have a look at the install&management script repo at Tryton community. All kind of constructive critisism is very welcome.

1 Like

I added a section to run Gunicorn as a different user which have only read and execute rights. Also added a note to byte compile everything as the trysysuser.

Personally I don’t think it’s a huge issue when a running user can modify the source code. When that is taking place, the system is already compromised because the user doesn’t have a login possibility so you have to do it through sudo su - trysysuser. And when someone is already root …

Even Docker isn’t a secure environment because of it’s daemon running as root on the system. So when someone breaks out of the container he will be root on your system and you may hope SELinux or Apparmor will respond.

1 Like

You do not need to use the docker daemon to run docker image (ex: https://podman.io/).
And you can run docker without root.

Anyone with access to the docker host can access the docker images as root, update a text editor and update the source code. Even can copy the modified files from an external services.

So docker has exactly the same problems as virtualenv as means of editing source code, which IMHO are not a real problem but just a side efect of using python code.

This is not at all the threat I’m talking about.
It is about the application that can modify itself.

Is there any tryton code that modifies its own source code? I do not know any code in our public code that has such feature.

I hope that there is no body writing such code.

So at the end we are talking about something that nobody knows it exist?

It seems we are shoting ourselves in our own foots! :person_facepalming:

W^X is a basic memory security measure. The same measure applies to interpreted source code.

That’s what security is all about.
Protecting yourself from unknown attack vectors.

It’s just basic stuff really.

When I say we are shoting in our own foots, I do not reference the security because I perfectly understand that anyone can install malicious code in a modular system (which is not easy on tryton TBH) but the fact that we are adding negative comments to a solution which may be perfectly valid. Sorry for the missunderstanding.

I will explain what I think it will be more inclusive. Instead of saying:

As the problem is not related to docker or other means. It is related to the user executing the process. I think its better to make a more constructive comment like:

“Updating the tutorial to use a different user will make it safer”.

But at the end this is already solved in:

If there is something that can be done in improving tryton security there is the issue tracker for that and in any case it should be worker for all deployment options that follow good security advices, not just for docker.

P.S: It seems we ended having another useless discussion? :thinking:

It was useful for me.
I understand that limiting application rights on its own code would be an improvement to my current implementation.
I don’t need a exploit to consider this point, but your thought about the difficulty to create an exploit is interesting too, at least for me. I’ll make my own decision now.

For those not using docker (for any reason), there is another simple solution:

Have one non-root user for managing the installation and another non-root user to run the application.