Launch QuestDB with systemd
Use systemd to run QuestDB as a service. For production deployments, we strongly recommend a system service running as a dedicated, unprivileged user. It starts at boot without depending on an interactive login and lets systemd apply the required resource limits directly.
This guide provides system-service examples for QuestDB Open Source and QuestDB Enterprise.
Prerequisites
The prerequisites for deploying QuestDB with systemd are:
- A 64-bit Linux system (x86-64 or ARM64) running systemd
- The QuestDB archive for your edition and architecture. Runtime (
rt-) archives bundle their own Java and install it at/opt/questdb/bin/java. The no-JRE archive shipsquestdb.jaralone and runs on your system Java, so it also requires Java 25 andunzip. QuestDB Open Source on ARM64 is only distributed as the no-JRE archive.
The archive you install determines which unit to use below, so keep track of
which one you extracted to /opt/questdb.
Initial system configuration
The command blocks below are chained with && so that each one pastes as a
single command. Where /etc/sudoers sets use_pty, which is the default on
Debian 13, Ubuntu 24.04 and RHEL 9, sudo runs its command in a new pty and
consumes whatever is still buffered in the terminal. Pasting a block of separate
sudo lines therefore runs the first line and silently discards the rest,
leaving a half-finished install that only surfaces later as a status=203/EXEC
failure.
Create a dedicated system user and group. The unit will create and manage its
QuestDB root directory at /var/lib/questdb. If the questdb user already
exists, skip the useradd command.
sudo useradd --system --user-group \
--home-dir /var/lib/questdb \
--shell /usr/sbin/nologin \
questdb
For QuestDB Open Source, select your server architecture:
- x86-64
- ARM64
Download the current Linux runtime and extract it to /opt/questdb:
curl -fL https://github.com/questdb/questdb/releases/download/10.0.0/questdb-10.0.0-rt-linux-x86-64.tar.gz -o questdb.tar.gz &&
sudo install -d -o root -g root -m 0755 /opt/questdb &&
sudo tar -xzf questdb.tar.gz -C /opt/questdb --strip-components 1 &&
sudo chown -R root:root /opt/questdb
The no-JRE archive also runs on x86-64, if you would rather use your own Java 25
installation than the bundled runtime. Follow the ARM64 steps, but extract the
linux-x86-64 native libraries instead of the linux-aarch64 ones, and use the
"No-JRE archive" unit in Example questdb.service.
On ARM64, use the no-JRE archive. Install Java 25 and unzip with your package
manager, then download and extract QuestDB:
curl -fL https://github.com/questdb/questdb/releases/download/10.0.0/questdb-10.0.0-no-jre-bin.tar.gz -o questdb.tar.gz &&
sudo install -d -o root -g root -m 0755 /opt/questdb &&
sudo tar -xzf questdb.tar.gz -C /opt/questdb --strip-components 1 &&
sudo install -d -o root -g root -m 0755 /opt/questdb/lib &&
sudo unzip -jo /opt/questdb/questdb.jar 'io/questdb/bin/linux-aarch64/*.so' -d /opt/questdb/lib &&
sudo chown -R root:root /opt/questdb
The native libraries are installed under /opt/questdb/lib so that QuestDB can
load them directly instead of extracting them to the system temporary directory.
For QuestDB Enterprise, extract the runtime archive for your architecture to
/opt/questdb. Run these commands from the directory containing the archive:
- x86-64
- ARM64
sudo install -d -o root -g root -m 0755 /opt/questdb &&
sudo tar -xzf questdb-enterprise-*-rt-linux-amd64.tar.gz -C /opt/questdb --strip-components 1 &&
sudo chown -R root:root /opt/questdb
sudo install -d -o root -g root -m 0755 /opt/questdb &&
sudo tar -xzf questdb-enterprise-*-rt-linux-aarch64.tar.gz -C /opt/questdb --strip-components 1 &&
sudo chown -R root:root /opt/questdb
SELinux
If you have SELinux enabled, apply the default SELinux labels after extracting QuestDB:
sudo restorecon -R /opt/questdb
Using a QuestDB server.conf
With this unit, QuestDB creates /var/lib/questdb/conf/server.conf with default
settings on first start. See the
configuration reference for available options.
Example questdb.service
Write the unit for your edition to /etc/systemd/system/questdb.service. Each
command below creates that file in place, so it can be pasted into a terminal as
it stands. The examples set QDB_ROOT to /var/lib/questdb; QuestDB stores its
conf, db, log, and public directories beneath it. Adjust the
installation paths if necessary, then paste the block.
- QuestDB
- QuestDB Enterprise
- Runtime archive
- No-JRE archive
The runtime (rt-) archive bundles its own Java at /opt/questdb/bin/java:
sudo tee /etc/systemd/system/questdb.service > /dev/null <<'EOF'
[Unit]
Description=QuestDB
Documentation=https://questdb.com/docs/deployment/systemd/
[Service]
Type=exec
User=questdb
Group=questdb
Restart=always
RestartSec=2
KillSignal=SIGTERM
SuccessExitStatus=143
# QuestDB root directory
Environment=QDB_ROOT=/var/lib/questdb
StateDirectory=questdb
StateDirectoryMode=0750
ExecStartPre=/usr/bin/mkdir -p ${QDB_ROOT}/db
ExecStart=/opt/questdb/bin/java \
-DQuestDB-Runtime-66535 \
-Dcontainerized=false \
-ea -Dnoebug \
-XX:ErrorFile=${QDB_ROOT}/db/hs_err_pid+%%p.log \
-XX:+UnlockExperimentalVMOptions \
-XX:+AlwaysPreTouch \
-XX:+UseParallelGC \
--sun-misc-unsafe-memory-access=allow \
--enable-native-access=io.questdb \
--add-opens=java.base/java.lang=io.questdb \
--add-opens=java.base/java.lang.reflect=io.questdb \
--add-opens=java.base/java.nio=io.questdb \
--add-opens=java.base/java.time.zone=io.questdb \
--add-exports=java.base/jdk.internal.vm=io.questdb \
-m io.questdb/io.questdb.ServerMain \
-d ${QDB_ROOT}
# Raise the open-files limit to QuestDB's recommended value
LimitNOFILE=1048576
ProtectSystem=full
StandardOutput=journal
StandardError=journal
SyslogIdentifier=questdb
[Install]
WantedBy=multi-user.target
EOF
The no-JRE archive has no bundled Java, so this unit runs your system Java and
puts questdb.jar on the module path. Use it on ARM64, and on x86-64 whenever
you installed the no-JRE archive:
sudo tee /etc/systemd/system/questdb.service > /dev/null <<'EOF'
[Unit]
Description=QuestDB
Documentation=https://questdb.com/docs/deployment/systemd/
[Service]
Type=exec
User=questdb
Group=questdb
Restart=always
RestartSec=2
KillSignal=SIGTERM
SuccessExitStatus=143
# QuestDB root directory
Environment=QDB_ROOT=/var/lib/questdb
StateDirectory=questdb
StateDirectoryMode=0750
ExecStartPre=/usr/bin/mkdir -p ${QDB_ROOT}/db
ExecStart=/usr/bin/java \
-DQuestDB-Runtime-66535 \
-Dcontainerized=false \
-Dquestdb.libs.dir=/opt/questdb/lib \
-ea -Dnoebug \
-XX:ErrorFile=${QDB_ROOT}/db/hs_err_pid+%%p.log \
-XX:+UnlockExperimentalVMOptions \
-XX:+AlwaysPreTouch \
-XX:+UseParallelGC \
--sun-misc-unsafe-memory-access=allow \
--enable-native-access=io.questdb \
--add-opens=java.base/java.lang=io.questdb \
--add-opens=java.base/java.lang.reflect=io.questdb \
--add-opens=java.base/java.nio=io.questdb \
--add-opens=java.base/java.time.zone=io.questdb \
--add-exports=java.base/jdk.internal.vm=io.questdb \
-p /opt/questdb/questdb.jar \
-m io.questdb/io.questdb.ServerMain \
-d ${QDB_ROOT}
# Raise the open-files limit to QuestDB's recommended value
LimitNOFILE=1048576
ProtectSystem=full
StandardOutput=journal
StandardError=journal
SyslogIdentifier=questdb
[Install]
WantedBy=multi-user.target
EOF
questdb.jar is not on the default module path, so
-p /opt/questdb/questdb.jar is required to resolve the io.questdb module.
Point -Dquestdb.libs.dir at the directory you extracted the native libraries
into, which holds the linux-x86-64 set on x86-64 rather than the
linux-aarch64 one. Omit the flag to let QuestDB unpack the libraries to the
system temporary directory instead.
sudo tee /etc/systemd/system/questdb.service > /dev/null <<'EOF'
[Unit]
Description=QuestDB Enterprise
Documentation=https://questdb.com/docs/deployment/systemd/
[Service]
Type=exec
User=questdb
Group=questdb
Restart=always
RestartSec=2
KillSignal=SIGTERM
SuccessExitStatus=143
# QuestDB root directory
Environment=QDB_ROOT=/var/lib/questdb
StateDirectory=questdb
StateDirectoryMode=0750
ExecStartPre=/usr/bin/mkdir -p ${QDB_ROOT}/db
ExecStart=/opt/questdb/bin/java \
-DQuestDB-Runtime-66535 \
-Dcontainerized=false \
-ea -Dnoebug \
-XX:ErrorFile=${QDB_ROOT}/db/hs_err_pid+%%p.log \
-XX:+UnlockExperimentalVMOptions \
-XX:+AlwaysPreTouch \
-XX:+UseParallelGC \
--sun-misc-unsafe-memory-access=allow \
--enable-native-access=io.questdb,com.questdb \
--add-opens=java.base/java.lang=io.questdb,com.questdb \
--add-opens=java.base/java.lang.reflect=io.questdb,com.questdb \
--add-opens=java.base/java.nio=io.questdb,com.questdb \
--add-opens=java.base/java.time.zone=io.questdb,com.questdb \
--add-exports=java.base/jdk.internal.vm=io.questdb,com.questdb \
-m com.questdb/com.questdb.EntServerMain \
-d ${QDB_ROOT}
# Raise the open-files limit to QuestDB's recommended value
LimitNOFILE=1048576
ProtectSystem=full
StandardOutput=journal
StandardError=journal
SyslogIdentifier=questdb
[Install]
WantedBy=multi-user.target
EOF
-XX:ErrorFile writes JVM crash logs beneath QDB_ROOT/db; %%p is
intentional.
SuccessExitStatus=143 records systemctl stop as a successful orderly
shutdown.
Configure vm.max_map_count and other recommended OS settings separately. See
OS configuration.
Reload systemd, enable QuestDB at boot, and start it now:
sudo systemctl daemon-reload &&
sudo systemctl enable --now questdb.service
Check the service status:
sudo systemctl status questdb.service
View its journal:
sudo journalctl --unit=questdb.service --follow
Startup failures
If the service fails immediately, check the journal for either of the following
errors. Both mean the ExecStart directive does not match the archive installed
at /opt/questdb.
sudo journalctl --unit=questdb.service --no-pager -n 20
status=203/EXEC, alongside
Failed at step EXEC spawning /opt/questdb/bin/java: No such file or directory,
means that path does not exist. Check what /opt/questdb actually holds:
ls /opt/questdb
An empty or partial directory means the archive never extracted; re-run the
extraction commands from
Initial system configuration. A directory
holding questdb.jar rather than bin/ means the no-JRE archive is installed,
so either use the no-JRE unit above or install the runtime archive instead.
java.lang.module.FindException: Module io.questdb not found means ExecStart
runs a system Java without a module path. Add -p /opt/questdb/questdb.jar
before the -m io.questdb/io.questdb.ServerMain argument, as the no-JRE
ExecStart above does.
Unexpected restarts
If QuestDB restarts without an error, check the systemd journal:
sudo journalctl --unit=questdb.service --since "today"
On Ubuntu, package updates may restart affected services through needrestart,
including during unattended upgrades. See Ubuntu's
automatic updates documentation
for configuration options.