{"id":1948,"date":"2025-01-10T00:35:27","date_gmt":"2025-01-09T23:35:27","guid":{"rendered":"https:\/\/hobbykeller.spdns.de\/?p=1948"},"modified":"2025-01-29T12:52:35","modified_gmt":"2025-01-29T11:52:35","slug":"installing-tryton-as-a-docker-stack","status":"publish","type":"post","link":"https:\/\/hobbykeller.spdns.de\/?p=1948","title":{"rendered":"Installing Tryton as a Docker stack"},"content":{"rendered":"\n<p>This post kicks of a new series on the <a href=\"http:\/\/tryton.org\" data-type=\"link\" data-id=\"tryton.org\">Tryton<\/a> <acronym title=\"free open source software\">FOSS<\/acronym> <acronym title=\"enterprise resource planning\">ERP<\/acronym> system. Today&#8217;s quick post is about installing Tryton as a docker stack. As usual, the most tricky part is to come up with a functioning <code>docker-compose.yml<\/code> file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tryton installation options<\/h2>\n\n\n\n<p>According to <a href=\"https:\/\/www.tryton-dach.org\/tryton-buch-grundlagen\">Tryton DACH&#8217;s Tryton Buch<\/a>, there are 3 basic ways to install Tryton on Debian flavored systems:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>classic <code>apt<\/code> installation directly into the main operating system<\/li>\n\n\n\n<li>installation into a virtual Python environment via the <code>pip<\/code> command &#8211; Note that the German term &#8220;virtuelle Umgebung&#8221; refers to a Python venv and not to a virtual machine like VirtualBox or multipass.<\/li>\n\n\n\n<li>installation inside a Docker container or stack<\/li>\n<\/ol>\n\n\n\n<p>While Tryton DACH suggests to go for method 2 and provides scripts to automate the setup, maintenance and backup inside a Python virtual environment, my preference is clearly the Docker stack for it offers the best encapsulation: Not only is the stack nicely separated from the host system but it also groups together all necessary services. This enormously facilitates classic production tasks such as backup, transfer and restore of the Tryton installation. Additionally, it can easily combined with other docker services such as nginx reverse proxy for exposing it effortlessly but still safe to the outside world.<\/p>\n\n\n\n<p>The only disadvantage of method 3 is that there is no official or reliable Docker image for Raspberry ARM architectures.<\/p>\n\n\n\n<div class=\"wp-block-simple-alerts-for-gutenberg-alert-boxes sab-alert sab-alert-primary\" role=\"alert\">Provided that the Pi&#8217;s RAM is sufficient, it should not be too difficult to build a custom Docker image for ARM architectures. All we have to do is start from a standard Linux Alpine (or Debian) image, set up a venv and &#8220;pip&#8221; the Tryton Python packages into the image &#8211; which can basically done along the lines established by the script in method 2.<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">\u00d7<\/span><\/button><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Useful resources<\/h2>\n\n\n\n<p>Most online documentations on Tryton as a Docker service are geared towards running Tryton as separate Docker containers via the <code>docker run<\/code> command rather than as a stack via <code>docker compose<\/code>. Even the instructions for the <a href=\"https:\/\/hub.docker.com\/r\/tryton\/tryton\/\">tryton\/tryton<\/a> repository on the Docker hub are quite thin-lipped when it comes to docker compose. The reader is mainly referred to a sample <code>compose.yml<\/code> file which I could not get working.<\/p>\n\n\n\n<p>The best point to start from I could find is a docker-compose.yml file from the <a href=\"https:\/\/github.com\/Chris927\/tryton-docker-compose\/blob\/main\/docker-compose.yml\">tryton-docker-compose repo<\/a> on GitHub by user Chris927, which again was forked from <a href=\"https:\/\/github.com\/ehemmerlin\/tryton\">Eric Hemmerlin&#8217;s tryton repo<\/a> on GitHub. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step by step installation<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Prepare a <code>docker-compose.yml<\/code> file<\/h3>\n\n\n\n<p>This is by far the most important step. As already mentioned, we start from the template provided by GitHub user Chris927. The things we modify are:<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Warning messages issued by postgres service<\/h4>\n\n\n\n<p>The command for running the healthcheck test of the <code>postgres<\/code> service produces a series of debug messages <code>Fatal: role \"root\" does not exist<\/code>. The reason these warnings pop up is that the following line fails:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default decode:true \">test: [\"CMD-SHELL\", \"pg_isready\", \"-h\", \"localhost\", \"-p\", \"5432\", \"-U\", \"healthcheck-user\"]<\/pre><\/div>\n\n\n\n<p>There is a <a href=\"https:\/\/github.com\/peter-evans\/docker-compose-healthcheck\/issues\/16\">discussion<\/a> on how to appropriately expand environment variables in docker YAML files (not sure if it&#8217;s only related to the healthcheck feature or a general problem). The suggestion which solved the issue for me was to change the line as follows:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:default mark:2 decode:true \">    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}\"]\n      interval: 3s<\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Removal of mailhog service<\/h4>\n\n\n\n<p>I don&#8217;t know much about the purpose of the <code>mailhog<\/code> service which is included in the original compose file. On its GitHub page, <code>mailhog<\/code> is &#8211; among others &#8211; described as an &#8220;email testing tool for developers&#8221;. <\/p>\n\n\n\n<p>As our aim is to come up with a dockerized Tryton for production and not for development purposes and as for the time being, there is no evidence of an extensive need for operating SMTP services out of Tryton, I removed the complete <code>mailhog<\/code> section from the services list of the <code>docker-compose.yaml<\/code> file.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"height-set:true lang:default decode:true \">services:\n  postgres:\n    image: postgres:${POSTGRES_VERSION:-latest}\n    environment:\n      - POSTGRES_DB=${POSTGRES_DB:-tryton}\n      - POSTGRES_USER=${POSTGRES_USER}\n      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}\n    volumes:\n      - db:\/var\/lib\/postgresql\/data\n    restart: always\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}\"]\n      interval: 3s\n      timeout: 5s\n      retries: 15\n  app:\n    image: tryton\/tryton:${TRYTON_TAG}\n    environment:\n      - TRYTOND_EMAIL__FROM=${EMAIL_FROM}\n      - TRYTOND_DATABASE__URI=postgresql:\/\/${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres\/${POSTGRES_DB}\n    ports:\n      - \"8000:8000\"\n    volumes:\n      - data:\/var\/lib\/trytond\/db\n    depends_on:\n      postgres:\n        condition: service_healthy\n    restart: always\n  cron:\n    image: tryton\/tryton:${TRYTON_TAG}\n    command: trytond-cron --dev -v\n    environment:\n      - TRYTOND_DATABASE__URI=postgresql:\/\/${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres\/${POSTGRES_DB}\n    volumes:\n      - data:\/var\/lib\/trytond\/db\n    depends_on:\n      postgres:\n        condition: service_healthy\n  update:\n    image: tryton\/tryton:${TRYTON_TAG}\n    command: sh -c \"trytond-admin -d ${POSTGRES_DB} --all -v &amp;&amp; tail -f \/dev\/null\"\n    environment:\n      - TRYTOND_DATABASE__URI=postgresql:\/\/${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres\/${POSTGRES_DB}\n    volumes:\n      - data:\/var\/lib\/trytond\/db\n    depends_on:\n      postgres:\n        condition: service_healthy\n    restart: \"no\"\n\nvolumes:\n  db: {}\n  data: {}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">2. Prepare the <code>.env<\/code> file<\/h3>\n\n\n\n<p>While most environment variables in the docker-compose.yml file come with a default value, it looks reasonable to set some values in the .env file:<\/p>\n\n\n\n<div class=\"wp-block-simple-alerts-for-gutenberg-alert-boxes sab-alert sab-alert-warning\" role=\"alert\">When setting variables in the .env file avoid using the % sign (e.g. in passwords), as this can cause parsing errors in modules such as psycopg2.<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">\u00d7<\/span><\/button><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>TRYTON_TAG=7.0\nPOSTGRES_VERSION=16\nPOSTGRES_DB=tryton_db\nPOSTGRES_USER=tryton_db_user\nPOSTGRES_PASSWORD=testing123\nEMAIL_FROM=libudja@web.de<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3. Set up the admin user<\/h3>\n\n\n\n<p>Note that the <code>POSTGRES_USER<\/code> and <code>POSTGRES_PASSWORD<\/code> in the above <code>.env<\/code> file are the credentials for the admin superuser of the Postgres database. They don&#8217;t have anything to do with the Tryton user authentication and authorization. <\/p>\n\n\n\n<p>Start the docker stack by running <code>docker compose up -d<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-simple-alerts-for-gutenberg-alert-boxes sab-alert sab-alert-warning\" role=\"alert\">While you can already access the login page of your Tryton installation from your web browser at http:\/\/localhost:8000, you will <strong>not<\/strong> be able to login with the credentials you set in the .env file.<br>You <strong>must<\/strong> set the login password for the Tryton (not postgres) <strong>admin<\/strong> user as shown below:<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">\u00d7<\/span><\/button><\/div>\n\n\n\n<p>From the directory holding the <code>docker-compose.yml<\/code> file, execute <code>trytond-admin<\/code> to set the Tryton login password for the admin user:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker exec -it tryton-app-1 trytond-admin -d tryton_db --password<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>tryton_chris972-app-1<\/code> is the name of the Tryton container running the main Tryton app.<\/li>\n\n\n\n<li><code>-d tryton<\/code> specifies the name of the Tryton database as set in the <code>POSTGRES_DB<\/code> variable in the <code>.env<\/code> file.<\/li>\n<\/ul>\n\n\n\n<p>You will be asked to set a password for the Tryton superuser admin and confirm the password. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Initial login into Tryton as admin<\/h3>\n\n\n\n<p>The <strong>only<\/strong> combination of credentials that allow a login after a fresh installation are for user <code>admin<\/code> and with the password you just set in step 3.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2025\/01\/Screenshot-from-2025-01-10-00-26-44.png\"><img loading=\"lazy\" decoding=\"async\" width=\"898\" height=\"602\" src=\"https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2025\/01\/Screenshot-from-2025-01-10-00-26-44.png\" alt=\"\" class=\"wp-image-1961\" srcset=\"https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2025\/01\/Screenshot-from-2025-01-10-00-26-44.png 898w, https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2025\/01\/Screenshot-from-2025-01-10-00-26-44-300x201.png 300w, https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2025\/01\/Screenshot-from-2025-01-10-00-26-44-768x515.png 768w\" sizes=\"auto, (max-width: 898px) 100vw, 898px\" \/><\/a><\/figure>\n\n\n\n<p>Initial login screen of Tryton after a fresh install<\/p>\n\n\n\n<p>After entering the password in the next window, Tryton will walk you through a basic setup of your ERP. This however will be covered in a later blog post.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Having a closer look at the PostgreSQL database<\/h2>\n\n\n\n<p>You can access the PostgreSQL database directly from the command line with the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ docker exec -it tryton-postgres-1 psql -d tryton_db -U tryton_db_user<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>tryton_chris972_postgres-1<\/code> is the name of the container running the PostgreSQL server<\/li>\n\n\n\n<li><code>-d tryton<\/code> is the Tryton database as specified in the <code>POSTGRES_DB<\/code> variable of your <code>.env<\/code> file<\/li>\n\n\n\n<li><code>-U ilek<\/code> is the PostgreSQL admin user as specified in the <code>POSTGRES_USER<\/code> variable of your <code>.env<\/code> file. Once again: The POSTGRES_USER is the superuser of the PostgreSQL server. It does not refer to any user that can log into the Tryton system.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Is it possible to run Tryton on a Raspberry Pi?<\/h2>\n\n\n\n<p>Running Tryton on a Raspberry Pi machine would require an image that has been compiled for the Raspberry&#8217;s ARM architecture. While there are some ARM images on the Docker hub, these are inofficial builds with sparse to non-existant maintenance and outdated versions. I would therefore not recommend to run Tryton on such a machine.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post kicks of a new series on the Tryton FOSS ERP system. Today&#8217;s quick post is about installing Tryton as a docker stack. As usual, the most tricky part<span class=\"more-button\"><a href=\"https:\/\/hobbykeller.spdns.de\/?p=1948\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\">Installing Tryton as a Docker stack<\/span><\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[374,64,254],"tags":[376,338,377,375],"class_list":["post-1948","post","type-post","status-publish","format-standard","hentry","category-erp","category-linux","category-python","tag-compose","tag-docker","tag-erp","tag-tryton"],"_links":{"self":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts\/1948","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1948"}],"version-history":[{"count":18,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts\/1948\/revisions"}],"predecessor-version":[{"id":1976,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts\/1948\/revisions\/1976"}],"wp:attachment":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1948"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1948"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1948"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}