Shutting down QBittorrent + WebUI setup from systemctl can cause data loss #11371

Closed
opened 2026-02-21 21:25:58 -05:00 by deekerman · 11 comments
Owner

Originally created by @flying-sausages on GitHub (Dec 6, 2020).

Sorry about not having all the information requested here, but I'm working off of a sparse downstream ticket. https://github.com/swizzin/swizzin/issues/503

Please provide the following information

qBittorrent version and Operating System

Sadly, unknown, per context above

If on linux, libtorrent-rasterbar and Qt version

Sadly, unknown, per context above

What is the problem

Shutting down qbittorrent through the suggested systemd can cause data loss

What is the expected behavior

A way to gracefully shut down the process without causing data loss. I would assume that this might be done through a specific termination signal maybe but I cannot find any info on this. Either way, the default systemctl stop behaviour without a specific action is to send a SIGTERM, which does not seem to be handled properly in the setup described below.

It would be great to know whether there's a way to specify an ExecStop= or similar which would take care of this case.

Steps to reproduce

Follow this guide in the wiki and then run systemctl stop qbittorrent with active torrents loaded in qbittorrent.
Systemctl will shut down the process ungracefully

Extra info(if any)

N/A

Originally created by @flying-sausages on GitHub (Dec 6, 2020). Sorry about not having all the information requested here, but I'm working off of a _sparse_ downstream ticket. https://github.com/swizzin/swizzin/issues/503 **Please provide the following information** ### qBittorrent version and Operating System Sadly, unknown, per context above ### If on linux, libtorrent-rasterbar and Qt version Sadly, unknown, per context above ### What is the problem Shutting down qbittorrent through the suggested systemd can cause data loss ### What is the expected behavior A way to gracefully shut down the process without causing data loss. I would assume that this might be done through a specific termination signal maybe but I cannot find any info on this. Either way, the default `systemctl stop` behaviour without a specific action is to send a `SIGTERM`, which does not seem to be handled properly in the setup described below. It would be great to know whether there's a way to specify an `ExecStop=` or similar which would take care of this case. ### Steps to reproduce Follow [this guide in the wiki](https://github.com/qbittorrent/qBittorrent/wiki/Running-qBittorrent-without-X-server-(WebUI-only---systemd-service-setup,-Ubuntu-15.04-or-newer)) and then run `systemctl stop qbittorrent` with active torrents loaded in qbittorrent. `Systemctl` will shut down the process ungracefully ### Extra info(if any) N/A
deekerman 2026-02-21 21:25:58 -05:00
Author
Owner

@flying-sausages commented on GitHub (Dec 6, 2020):

I found some references to this elsewhere but no answers yet

@flying-sausages commented on GitHub (Dec 6, 2020): I found some references to this elsewhere but no answers yet - https://github.com/qbittorrent/qBittorrent/issues/4498#issuecomment-632222403
Author
Owner

@FranciscoPombal commented on GitHub (Dec 6, 2020):

Thanks for the report.

The systemd default is to send SIGTERM, even if ExecStop is not specified, like you said. As per github.com/qbittorrent/qBittorrent@3485ad39d9/src/app/main.cpp (L299), both SIGTERM and SIGINT are handled, and this is working correctly as far as I know.

Regardless, I have edited the wiki page in question to more closely match what I am using in one of my "production" machines. I chose SIGINT instead of the default, for consistency with termination from the shell (^C sends SIGINT), but this is purely an aesthetic decision AFAIK.

Perhaps there is some bug in the signal handling logic that I haven't been able to observe so far. @an0n666 can you reproduce it?

@FranciscoPombal commented on GitHub (Dec 6, 2020): Thanks for the report. The systemd default is to send `SIGTERM`, even if `ExecStop` is not specified, like you said. As per https://github.com/qbittorrent/qBittorrent/blob/3485ad39d9729e9ca106191b0e02719b46f5cfd8/src/app/main.cpp#L299, both `SIGTERM` and `SIGINT` are handled, and this is working correctly as far as I know. Regardless, I have edited the wiki page in question to more closely match what I am using in one of my "production" machines. I chose `SIGINT` instead of the default, for consistency with termination from the shell (`^C` sends `SIGINT`), but this is purely an aesthetic decision AFAIK. Perhaps there is some bug in the signal handling logic that I haven't been able to observe so far. @an0n666 can you reproduce it?
Author
Owner

@flying-sausages commented on GitHub (Dec 6, 2020):

Just to clear out any doubts, here is the service file we are currently using

[Unit]
Description=qBittorrent noX for %i
After=network.target

[Service]
Type=forking
User=%i
Group=%i
ExecStart=/usr/bin/qbittorrent-nox -d

[Install]
WantedBy=multi-user.target
@flying-sausages commented on GitHub (Dec 6, 2020): Just to clear out any doubts, here is the service file we are currently using ```ini /etc/systemd/system/qbittorrent@.service [Unit] Description=qBittorrent noX for %i After=network.target [Service] Type=forking User=%i Group=%i ExecStart=/usr/bin/qbittorrent-nox -d [Install] WantedBy=multi-user.target ```
Author
Owner

@ghost commented on GitHub (Dec 6, 2020):

@an0n666 can you reproduce it?

The process getting killed instantly when systemctl issues a stop command is reproducible.

However, after some investigation I found out the actual reason for the data loss:

My disk ran out of space. I cleaned out 150 GiB+ while qBt was still running.
In the meantime, qBt failed to write any fastresume data and zero-filled almost 300+ files(this I found out later when I went to check the BT_Backup after the data loss)
After making space I stopped the service to reboot my server.
But since the process was just insta killed qBt missed the opportunity to rewrite those zero-filled fastresume files during the shutdown process.

Additional issues: When I went to check the BT_backup folder I found 300+ fastresume files were only 0 byte. Then I had to delete those fastresume files because qBt won't even allow re-adding those torrents since it even loads empty fastresume files to session!

@ghost commented on GitHub (Dec 6, 2020): > @an0n666 can you reproduce it? > The process getting killed instantly when systemctl issues a stop command is reproducible. However, after some investigation I found out the actual reason for the data loss: My disk ran out of space. I cleaned out 150 GiB+ while qBt was still running. In the meantime, qBt failed to write any fastresume data and zero-filled almost 300+ files(this I found out later when I went to check the BT_Backup after the data loss) After making space I stopped the service to reboot my server. But since the process was just insta killed qBt missed the opportunity to rewrite those zero-filled fastresume files during the shutdown process. Additional issues: When I went to check the BT_backup folder I found 300+ fastresume files were only 0 byte. Then I had to delete those fastresume files because qBt won't even allow re-adding those torrents <s>since it even loads empty fastresume files to session!</s>
Author
Owner

@FranciscoPombal commented on GitHub (Dec 7, 2020):

The process getting killed instantly when systemctl issues a stop command is reproducible.

This definitely isn't supposed to happen. Any chance you can figure out what's wrong? Is there some kind of problem catching SIGINT or SIGTERM, or both?

@FranciscoPombal commented on GitHub (Dec 7, 2020): > The process getting killed instantly when systemctl issues a stop command is reproducible. This definitely isn't supposed to happen. Any chance you can figure out what's wrong? Is there some kind of problem catching SIGINT or SIGTERM, or both?
Author
Owner

@ghost commented on GitHub (Dec 7, 2020):

First I tried with the updated wiki and it was killing the process instantly.
After some trial and error, I found that this line was causing the issue
KillSignal=SIGINT
After removing it the process was gracefully killed.
Here's the final systemd unit file.

[Unit]
Description=qBittorrent noX for %i
After=network.target

[Service]
Type=simple
User=%i
Group=%i
ExecStart=/usr/bin/qbittorrent-nox
ExecStop=/usr/bin/pkill --signal SIGINT qbittorrent-nox
TimeoutStopSec=60 # default is 90 seconds

[Install]
WantedBy=multi-user.target

Note to swizzin devs: the process doesn't run as a daemon and there's no forking. So I suggest you to change the Type=simple and remove -d from the ExecStart.

@ghost commented on GitHub (Dec 7, 2020): First I tried with the updated wiki and it was killing the process instantly. After some trial and error, I found that this line was causing the issue `KillSignal=SIGINT` After removing it the process was gracefully killed. Here's the final systemd unit file. ``` [Unit] Description=qBittorrent noX for %i After=network.target [Service] Type=simple User=%i Group=%i ExecStart=/usr/bin/qbittorrent-nox ExecStop=/usr/bin/pkill --signal SIGINT qbittorrent-nox TimeoutStopSec=60 # default is 90 seconds [Install] WantedBy=multi-user.target ``` Note to swizzin devs: the process doesn't run as a daemon and there's no forking. So I suggest you to change the Type=simple and remove -d from the ExecStart.
Author
Owner

@Chocobo1 commented on GitHub (Dec 7, 2020):

We have an official .service file here: https://github.com/qbittorrent/qBittorrent/blob/master/dist/unix/systemd/qbittorrent-nox%40.service.in

@an0n666
Do you confirm that this line makes all the differences? ExecStop=/usr/bin/pkill --signal SIGINT qbittorrent-nox
If that is true, can you submit a patch?

ps. note that I would expect that qbt is identified by pid when sending the signal.
pps. If possible, I would expect sending SIGTERM instead of SIGINT.
ppps. Could be something like: ExecStop=/usr/bin/kill $MAINPID

@Chocobo1 commented on GitHub (Dec 7, 2020): We have an official .service file here: https://github.com/qbittorrent/qBittorrent/blob/master/dist/unix/systemd/qbittorrent-nox%40.service.in @an0n666 Do you confirm that this line makes all the differences? `ExecStop=/usr/bin/pkill --signal SIGINT qbittorrent-nox` If that is true, can you submit a patch? ps. note that I would expect that qbt is identified by pid when sending the signal. pps. If possible, I would expect sending `SIGTERM` instead of `SIGINT`. ppps. Could be something like: `ExecStop=/usr/bin/kill $MAINPID`
Author
Owner

@ghost commented on GitHub (Dec 7, 2020):

Do you confirm that this line makes all the differences? ExecStop=/usr/bin/pkill --signal SIGINT qbittorrent-nox`

Ok so I just removed that line as well and it's working fine. Just changing these were enough:

Type=simple
ExecStart=/usr/bin/qbittorrent-nox

So the script in the wiki(previous revision) would've worked just fine. idk why swizzin changed the type to forking.

You can close this.

@ghost commented on GitHub (Dec 7, 2020): >Do you confirm that this line makes all the differences? ExecStop=/usr/bin/pkill --signal SIGINT qbittorrent-nox` > Ok so I just removed that line as well and it's working fine. Just changing these were enough: ``` Type=simple ExecStart=/usr/bin/qbittorrent-nox ``` So the script in the wiki(previous revision) would've worked just fine. idk why swizzin changed the type to forking. You can close this.
Author
Owner

@liaralabs commented on GitHub (Dec 8, 2020):

If this is true, then that means the daemon mode isn't gracefully handling the SIGTERM signal and instantly closing qbittorrent as you claim.

The script is forking because a daemon process forks off the main PID, and this setting informs systemd of that so that it monitors the child PID instead.

I can't recall why I chose daemon mode though -- it may have been to avoid potential issues with accepting the legal agreement and scripting, which I can't recall if I actually tested, to be fair.

@liaralabs commented on GitHub (Dec 8, 2020): If this is true, then that means the daemon mode isn't gracefully handling the SIGTERM signal and instantly closing qbittorrent as you claim. The script is forking because a daemon process forks off the main PID, and this setting informs systemd of that so that it monitors the child PID instead. I can't recall why I chose daemon mode though -- it may have been to avoid potential issues with accepting the legal agreement and scripting, which I can't recall if I actually tested, to be fair.
Author
Owner

@userdocs commented on GitHub (Dec 8, 2020):

@FranciscoPombal there are also --user systemd services in ~/.config/systemd/user. I use this.

[Unit]
Description=qbittorrent
After=network-online.target

[Service]
Type=simple
ExecStart=%h/bin/qbittorrent-nox
Restart=always
RestartSec=2
TimeoutStopSec=5
SyslogIdentifier=qbittorrent

[Install]
WantedBy=default.target
@userdocs commented on GitHub (Dec 8, 2020): @FranciscoPombal there are also `--user` systemd services in `~/.config/systemd/user`. I use this. ``` [Unit] Description=qbittorrent After=network-online.target [Service] Type=simple ExecStart=%h/bin/qbittorrent-nox Restart=always RestartSec=2 TimeoutStopSec=5 SyslogIdentifier=qbittorrent [Install] WantedBy=default.target
Author
Owner

@FranciscoPombal commented on GitHub (Dec 8, 2020):

After some experimentation, it does seem like using SIGINT is flakier, though it seemed spurious and I'm not sure why, haven't looked into it. I have edited the wiki page to improve the service file.

It now boils down to this:

Description=qBittorrent-nox service
Documentation=man:qbittorrent-nox(1)
Wants=network-online.target
After=network-online.target nss-lookup.target

[Service]
Type=exec
User=qbtuser
ExecStart=/usr/bin/qbittorrent-nox

[Install]
WantedBy=multi-user.target

This uses the systemd defaults for terminating the process (SIGTERM). This is pretty much the same as our "offiicial" bundled script now, except that the Type is exec (which is better than simple for our use case in systems with recent enough systemd to support this type), and also it does not specify PrivateTmp=false, which is the default anyway, so it really does not need to be there.

I can confirm that this config works well (qBittorrent exits gracefully), whether stopping qBittorrent via systemd or via its own menu in the WebUI.


@liaralabs

The script is forking because a daemon process forks off the main PID, and this setting informs systemd of that so that it monitors the child PID instead.

I can't recall why I chose daemon mode though -- it may have been to avoid potential issues with accepting the legal agreement and scripting, which I can't recall if I actually tested, to be fair.

With the configuration in this post, you don't need daemon mode (the -d flag), in fact, it would not even be correct and could potentially be a source of problems.


@userdocs

A user service is also a good idea. If you want, you could contribute a wiki page with that information, or add it to the existing one as an alternative.

@FranciscoPombal commented on GitHub (Dec 8, 2020): After some experimentation, it does seem like using SIGINT is flakier, though it seemed spurious and I'm not sure why, haven't looked into it. I have edited the wiki page to improve the service file. It now boils down to this: ```[Unit] Description=qBittorrent-nox service Documentation=man:qbittorrent-nox(1) Wants=network-online.target After=network-online.target nss-lookup.target [Service] Type=exec User=qbtuser ExecStart=/usr/bin/qbittorrent-nox [Install] WantedBy=multi-user.target ``` This uses the systemd defaults for terminating the process (SIGTERM). This is pretty much the same as our "offiicial" bundled script now, except that the `Type` is exec (which is better than simple for our use case in systems with recent enough systemd to support this type), and also it does not specify `PrivateTmp=false`, which is the default anyway, so it really does not need to be there. I can confirm that this config works well (qBittorrent exits gracefully), whether stopping qBittorrent via systemd or via its own menu in the WebUI. --- @liaralabs > The script is forking because a daemon process forks off the main PID, and this setting informs systemd of that so that it monitors the child PID instead. > I can't recall why I chose daemon mode though -- it may have been to avoid potential issues with accepting the legal agreement and scripting, which I can't recall if I actually tested, to be fair. With the configuration in this post, you don't need daemon mode (the `-d` flag), in fact, it would not even be correct and could potentially be a source of problems. --- @userdocs A user service is also a good idea. If you want, you could contribute a wiki page with that information, or add it to the existing one as an alternative.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/qBittorrent#11371
No description provided.