[Python] How can a Django app on a Raspberry Pi safely trigger an upgrade of itself using apt...

Discussão em 'Python' iniciado por Stack, Maio 6, 2025.

  1. Stack

    Stack Membro Participativo

    I'm distributing a Django/Python web application as a Debian package on a Raspberry Pi. I’d like to implement an "Upgrade" button in the web UI that triggers a sudo apt update && sudo apt -y install my_app to upgrade the application package.

    The challenge is that the app is trying to replace itself while it's still running — the very code initiating the upgrade is part of what will be upgraded.

    What I've tried:

    The Upgrade button sends a POST to the Django backend.

    The backend uses python subprocess to start a oneshot systemd unit (gui-package-update.service) that runs the upgrade commands (via sudo).

    The Django app itself is started on boot using another systemd service and runs as a non-root user.

    I've configured sudoers to allow the Django user to run sudo systemctl start gui-package-update.service without a password.

    A second POST api call is used to monitor the state of the upgrade, those states being:

    • running - the server is up and running
    • upgrading - the server has received the POST command to start an upgrade
    • 404/500 - the server is currently rebooting

    The 404/500 errors are caught in the JS front end and interpreted to mean that the server is "reloading".

    Problem:

    This approach works only the first time after a reboot. On subsequent attempts, the oneshot service does not run again. Although there are no services in the failed state, I've tried resetting the service using systemctl reset-failed, but it doesn't fix the issue.

    My suspicion is that the postinst script of the new Debian package runs systemctl daemon-reexec or systemctl daemon-reload, which interferes with the active systemd process launched by the app during the upgrade.

    Question:

    What is a reliable design pattern for allowing a Django app to trigger its own upgrade via a GUI button, considering the constraints of apt, systemd, and the fact that the running code is being replaced?

    I'm looking for approaches that:

    • Avoid conflicts between systemd reloads and running services
    • Allow repeated upgrades without requiring a reboot
    • Work cleanly with Debian packaging and postinst scripts

    I'm not going to insist on using a OneShot service, that's just the approach I tried.

    Here is an example of my unit service file:

    [Unit]
    Description=Update specific ODIN16 GUI and collateral packages
    After=network.target

    [Service]
    Type=oneshot
    ExecStart=/bin/sh -c 'apt update && apt-get install -y my_app'
    StandardOutput=append:/var/log/gui-package-update.log
    StandardError=append:/var/log/gui-package-update.log
    User=root
    Group=root
    RemainAfterExit=no
    Restart=no

    [Install]
    WantedBy=multi-user.target

    Continue reading...

Compartilhe esta Página