Self-hosted Bitwarden breaks when started via systemd

I have Bitwarden self-hosted on a Linode Debian 10 VM with 4 cores/8GB of RAM.

If I start Bitwarden via “bitwarden.sh start”, it works great. But if I start it via systemd, it goes into a constant crash-and-restart loop. ‘docker ps’ shows the containers restarting continuously, after only a few seconds.

None of the logs show any errors, though I see this in the MSSQL error.log:

2020-06-29 16:20:33.13 spid18s  SQL Server is now ready for client connections. This is an informational message; no user action is required.
2020-06-29 16:20:34.00 spid9s Starting up database 'tempdb'.
2020-06-29 16:20:34.42 spid21s  The Service Broker endpoint is in disabled or stopped state.
2020-06-29 16:20:34.42 spid21s  The Database Mirroring endpoint is in disabled or stopped state.
2020-06-29 16:20:34.44 spid21s  Service Broker manager has started.
2020-06-29 16:20:34.45 spid6s Recovery is complete. This is an informational message only. No user action is required.
2020-06-29 16:20:40.73 spid6s Always On: The availability replica manager is going offline because SQL Server is shutting down. This is an informational message only. No user action is required.
2020-06-29 16:20:40.73 spid6s SQL Server is terminating in response to a 'stop' request from Service Control Manager. This is an informational message only. No user action is required.
2020-06-29 16:20:41.57 spid21s  Service Broker manager has shut down.
2020-06-29 16:20:44.51 spid6s SQL Trace was stopped due to server shutdown. Trace ID = '1'. This is an informational message only; no user action is required.

Not a SQL Server DBA, but seems like MSSQL is the victim rather than the cause here. I did not see any other significant messages in other logs.

Here is my systemd unit file:

[Unit]
Description=Bitwarden
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStart=/bitwarden/bitwarden.sh start
ExecStop=/bitwarden/bitwarden.sh stop

[Install]
WantedBy=default.target

My exact sequence of steps to setup is:

mkdir /bitwarden
cd /bitwarden
apt install -y apt-transport-https ca-certificates wget gnupg2 software-properties-common
wget -O - https://download.docker.com/linux/debian/gpg | apt-key add
echo "deb [arch=amd64] https://download.docker.com/linux/debian buster stable" > /etc/apt/sources.list.d/docker.list
apt update
apt-cache madison docker-ce
apt install -y docker-ce docker-ce-cli docker-compose containerd.io
systemctl start docker
systemctl enable docker
curl -Lso bitwarden.sh https://go.btwrdn.co/bw-sh && chmod +x bitwarden.sh
./bitwarden.sh install
(I selected to use a Let's Encrypt cert)
Create the systemd unit file above in /etc/systemd/system/bitwarden.service
systemctl daemon-reload
systemctl enable bitwarden
systemctl start bitwarden

Is there a systemd directive I’m missing perhaps? Again, it runs fine without crashing or restarting if I invoke “bitwarden.sh start” on the command line, and I’m able to use the web front-end to create accounts, add entries, etc. But stopping and restarting with systemd results in a spin cycle.

Removing the Restart= parameter fixed this. Of course, this means Bitwarden won’t be restarted if it fails.

This makes sense - systemd has no way to know if the app is up or down. However, it would be nice if there was some way to include restart conditions.

One way to solve this issue is to include the following in the [Service] section.

Type=oneshot
RemainAfterExit=true

This solves the issue because bitwarden.sh is simply a script that starts/stops the Bitwarden Docker containers, and then exits after it has done so. The default Type=simple tells systemd to stop the service after the main process has exited. Since the script exits after it is done, systemd immediately stops the service after Bitwarden has been started. Type=oneshot is an option made specially for scripts that run once, and then exit (e.g. bitwarden.sh). RemainAfterExit=true tells systemd to still consider the service active, even after the main process has exited. The Arch Wiki page for systemd has a lot of good information, and expands on the explanation given here.