Skip to content

Commit 751e804

Browse files
committed
chore(docker,ci,docs): privilege-drop entrypoint, nextest in CI, expanded getting-started guide
Docker: the image no longer runs as root. A new docker-entrypoint.sh (using gosu) fixes ownership on the data volume when started as root, then drops to uid 10001 (nodedb) before exec-ing the server. When already started as a non-root user (--user 10001:10001) the entrypoint passes through directly. This makes named-volume mounts work on Linux hosts where Docker initialises volumes as root-owned. CI: the test workflow now installs cargo-nextest via taiki-e/install-action and runs cargo nextest run. Plain cargo test ignores the nextest.toml cluster test-group that serialises 3-node integration tests and would hang on the cluster suite. JUnit output is uploaded as an artifact on every run for post-mortem analysis. Docs: getting-started gains a prebuilt binary install section for Linux (x64 and arm64), plain docker run instructions alongside the existing Compose block, a systemd unit example, and a unified configuration reference that applies to all install methods. README test command updated to reflect nextest.
1 parent 1bd04da commit 751e804

5 files changed

Lines changed: 203 additions & 25 deletions

File tree

.github/workflows/test.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,21 @@ jobs:
5555
sudo apt-get install -y --no-install-recommends \
5656
cmake clang libclang-dev pkg-config protobuf-compiler perl \
5757
libcurl4-openssl-dev libsasl2-dev
58+
# nextest is required — `.config/nextest.toml` defines the
59+
# `cluster` test-group that serializes 3-node integration tests
60+
# and the `ci` profile that retries flaky cluster tests once and
61+
# writes a JUnit report. Plain `cargo test` ignores all of that
62+
# and will hang/fail on the cluster suite.
63+
- name: Install cargo-nextest
64+
uses: taiki-e/install-action@v2
65+
with:
66+
tool: nextest
5867
- name: Run tests
59-
run: cargo test --all-features --profile ci
68+
run: cargo nextest run --all-features --cargo-profile ci --profile ci
69+
- name: Upload JUnit report
70+
if: always()
71+
uses: actions/upload-artifact@v4
72+
with:
73+
name: junit-report
74+
path: target/nextest/ci/junit.xml
75+
if-no-files-found: ignore

Dockerfile

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ FROM debian:bookworm-slim AS runtime
3636

3737
# ca-certificates: needed for JWKS fetch, OTLP export, S3 archival
3838
# curl: needed for HEALTHCHECK
39+
# gosu: drop privileges from root after fixing data-dir ownership in entrypoint
3940
RUN apt-get update && apt-get install -y --no-install-recommends \
4041
ca-certificates \
4142
curl \
43+
gosu \
4244
&& rm -rf /var/lib/apt/lists/*
4345

4446
# Non-root user
@@ -51,12 +53,18 @@ RUN mkdir -p /var/lib/nodedb /etc/nodedb \
5153

5254
COPY --from=builder /build/target/release/nodedb /usr/local/bin/nodedb
5355

56+
# Entrypoint: when started as root, fix data-dir ownership and drop to the
57+
# nodedb user. When already started as a non-root user (e.g. `--user 10001`),
58+
# exec directly. This makes `-v <named-volume>:/var/lib/nodedb` work even
59+
# when Docker initialises the volume as root-owned (common on Linux hosts).
60+
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
61+
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
62+
5463
# Bind to all interfaces (required for Docker port mapping)
5564
# Point data dir at the declared volume
5665
ENV NODEDB_HOST=0.0.0.0 \
5766
NODEDB_DATA_DIR=/var/lib/nodedb
5867

59-
USER nodedb
6068
WORKDIR /var/lib/nodedb
6169

6270
# pgwire | native protocol | HTTP API | WebSocket sync | OTLP gRPC | OTLP HTTP
@@ -67,4 +75,5 @@ VOLUME ["/var/lib/nodedb"]
6775
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s \
6876
CMD curl -f http://localhost:6480/health || exit 1
6977

70-
ENTRYPOINT ["/usr/local/bin/nodedb"]
78+
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
79+
CMD ["/usr/local/bin/nodedb"]

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ For development or contributing:
148148
git clone https://github.com/NodeDB-Lab/nodedb.git
149149
cd nodedb
150150
cargo build --release
151-
cargo test --all-features
151+
cargo install cargo-nextest --locked # one-time
152+
cargo nextest run --all-features
152153
```
153154

154155
## Status

docker-entrypoint.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/sh
2+
# NodeDB container entrypoint.
3+
#
4+
# When invoked as root (the default for `docker run` with no --user), fix
5+
# ownership of NODEDB_DATA_DIR and drop privileges to the unprivileged
6+
# `nodedb` user before exec'ing the server. When invoked as any other UID
7+
# (e.g. `--user 10001` or via Kubernetes runAsUser), exec directly and
8+
# leave the data directory alone.
9+
#
10+
# This makes `-v <named-volume>:/var/lib/nodedb` work even when Docker
11+
# initialises the named volume as root-owned (common on Linux hosts where
12+
# the volume is created out-of-band before the container's first run).
13+
14+
set -e
15+
16+
DATA_DIR="${NODEDB_DATA_DIR:-/var/lib/nodedb}"
17+
18+
if [ "$(id -u)" = "0" ]; then
19+
# Running as root: ensure the data dir exists and is owned by nodedb,
20+
# then drop privileges. mkdir is a no-op for the declared VOLUME but
21+
# protects against custom NODEDB_DATA_DIR overrides.
22+
mkdir -p "$DATA_DIR"
23+
chown -R nodedb:nodedb "$DATA_DIR"
24+
exec gosu nodedb "$@"
25+
fi
26+
27+
# Already non-root: ensure we can actually write to the data dir, otherwise
28+
# fail fast with a clear message instead of the cryptic WAL "Permission
29+
# denied (os error 13)" the user sees on a misconfigured volume mount.
30+
if [ ! -w "$DATA_DIR" ]; then
31+
cat >&2 <<EOF
32+
nodedb: data directory $DATA_DIR is not writable by uid=$(id -u) gid=$(id -g).
33+
34+
This usually means a host volume was mounted with root ownership while
35+
NodeDB is configured to run as a non-root user. Fixes:
36+
37+
1. Let the entrypoint fix it: drop the explicit --user flag so the
38+
container starts as root and chowns the volume on first boot.
39+
2. Pre-create the volume with the right ownership on the host, e.g.
40+
chown -R 10001:10001 /path/to/host/dir
41+
3. Run as root explicitly: docker run --user 0:0 ...
42+
43+
EOF
44+
exit 1
45+
fi
46+
47+
exec "$@"

docs/getting-started.md

Lines changed: 126 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
# Getting Started
22

3-
This guide walks you through starting a NodeDB server and running your first queries.
3+
This guide walks you through starting a NodeDB server and running your first queries. NodeDB requires Linux kernel ≥ 5.1 (for io_uring) regardless of how you run it.
44

5-
## Run with Docker (recommended)
5+
There are three ways to install NodeDB:
66

7-
The fastest way to get started. Requires Linux kernel ≥ 5.1 (for io_uring).
7+
1. [Prebuilt binary](#run-a-prebuilt-binary-linux)**recommended on Linux.** Direct kernel access to io_uring, no virtualization overhead, best raw performance.
8+
2. [Docker](#run-with-docker)**recommended on macOS / Windows / WSL2**, or when you want a one-command setup with zero host configuration.
9+
3. [Build from source](#build-from-source) — for development or custom features.
10+
11+
All three share the same [configuration](#configuration), [connection](#connect), and [query](#first-queries) sections below.
12+
13+
## Run with Docker
14+
15+
The easiest way to get started, and the right choice on macOS, Windows, or any host where you don't want to manage a binary directly. On native Linux, the [prebuilt binary](#run-a-prebuilt-binary-linux) gives you better performance.
16+
17+
### Docker Compose
818

919
```bash
1020
docker compose up -d
@@ -24,6 +34,22 @@ To stop and wipe all data:
2434
docker compose down -v
2535
```
2636

37+
### Plain `docker run`
38+
39+
If you'd rather not use Compose:
40+
41+
```bash
42+
docker run -d --name nodedb \
43+
-p 6432:6432 \
44+
-p 6433:6433 \
45+
-p 6480:6480 \
46+
-p 9090:9090 \
47+
-v nodedb-data:/var/lib/nodedb \
48+
farhansyah/nodedb
49+
```
50+
51+
The container entrypoint runs as root just long enough to fix ownership on the data volume, then drops privileges to the `nodedb` user (uid 10001). To skip the root step, pass `--user 10001:10001` and pre-create the volume with matching ownership.
52+
2753
### Default ports
2854

2955
- **6432** — PostgreSQL wire protocol (pgwire)
@@ -61,6 +87,70 @@ Set them under `environment:` in `docker-compose.yml` or pass with `-e` to `dock
6187

6288
---
6389

90+
## Run a prebuilt binary (Linux)
91+
92+
Each tagged release ships a static `nodedb` tarball on GitHub for `linux-x64` and `linux-arm64`. macOS and Windows users should use Docker until those targets ship.
93+
94+
```bash
95+
# Resolve the latest tag and your architecture
96+
TAG=$(curl -fsSL https://api.github.com/repos/NodeDB-Lab/nodedb/releases/latest \
97+
| grep '"tag_name"' | cut -d'"' -f4)
98+
ARCH=$(uname -m | sed 's/aarch64/arm64/; s/x86_64/x64/')
99+
100+
# Download and extract
101+
curl -L -o nodedb.tar.gz \
102+
"https://github.com/NodeDB-Lab/nodedb/releases/download/${TAG}/nodedb-${TAG#v}-linux-${ARCH}.tar.gz"
103+
tar -xzf nodedb.tar.gz
104+
105+
# Optional: install system-wide
106+
sudo mv nodedb /usr/local/bin/
107+
108+
# Run with all defaults (data goes to ~/.nodedb/data)
109+
nodedb
110+
```
111+
112+
If you have the [GitHub CLI](https://cli.github.com/) installed, this is one command:
113+
114+
```bash
115+
gh release download --repo NodeDB-Lab/nodedb --pattern 'nodedb-*-linux-x64.tar.gz' \
116+
&& tar -xzf nodedb-*-linux-x64.tar.gz
117+
```
118+
119+
To run with a config file or a custom data directory:
120+
121+
```bash
122+
# Point at an explicit data dir
123+
NODEDB_DATA_DIR=/var/lib/nodedb nodedb
124+
125+
# Or load a config file (env vars still override TOML keys)
126+
nodedb --config /etc/nodedb/nodedb.toml
127+
```
128+
129+
For a long-running server, drop a unit file at `/etc/systemd/system/nodedb.service`:
130+
131+
```ini
132+
[Unit]
133+
Description=NodeDB
134+
After=network.target
135+
136+
[Service]
137+
Type=simple
138+
User=nodedb
139+
Group=nodedb
140+
ExecStart=/usr/local/bin/nodedb --config /etc/nodedb/nodedb.toml
141+
Restart=on-failure
142+
LimitNOFILE=1048576
143+
144+
[Install]
145+
WantedBy=multi-user.target
146+
```
147+
148+
Then `sudo systemctl enable --now nodedb`. The user/group must be able to read the config file and write `data_dir`.
149+
150+
> For a specific version or to browse changelogs, see the release page: <https://github.com/NodeDB-Lab/nodedb/releases>. The SQL surface is still pre-1.0 and changes between tags, so pin a version in production.
151+
152+
---
153+
64154
## Build from Source
65155

66156
```bash
@@ -70,8 +160,9 @@ cd nodedb
70160
# Release build (all crates)
71161
cargo build --release
72162
73-
# Run tests
74-
cargo test --all-features
163+
# Run tests (use nextest — see .config/nextest.toml)
164+
cargo install cargo-nextest --locked # one-time
165+
cargo nextest run --all-features
75166
```
76167

77168
Requires Rust 1.94+ and Linux (the Data Plane uses io_uring). The build produces two binaries:
@@ -84,8 +175,22 @@ Requires Rust 1.94+ and Linux (the Data Plane uses io_uring). The build produces
84175
```bash
85176
# Single-node, default ports
86177
./target/release/nodedb
178+
179+
# Or with a config file
180+
./target/release/nodedb --config nodedb.toml
87181
```
88182

183+
---
184+
185+
## Configuration
186+
187+
This section applies to **every** install method — Docker, prebuilt binary, and source builds all read the same TOML schema and respond to the same environment variables. Pick whichever is convenient:
188+
189+
- **TOML file** — pass `--config /path/to/nodedb.toml` on the command line. Best for production / systemd / pre-baked images.
190+
- **Environment variables** — prefix `NODEDB_*`. Best for Docker (`-e`), Compose (`environment:`), and Kubernetes. Env vars **override** values from the TOML file when both are set.
191+
192+
### Default ports
193+
89194
By default, NodeDB listens on:
90195

91196
- **6432** — PostgreSQL wire protocol (pgwire)
@@ -98,11 +203,11 @@ Two additional protocols are available but **disabled by default**:
98203
- **RESP** (Redis-compatible KV protocol) — `GET`/`SET`/`DEL`/`EXPIRE`/`SCAN`/`SUBSCRIBE`
99204
- **ILP** (InfluxDB Line Protocol) — high-throughput timeseries ingest
100205

101-
Enable them by setting a listen address in config or via env var (see below).
206+
Enable them by setting a listen address in the config or via env var (see below).
102207

103-
### Configuration
208+
### Example config file
104209

105-
All protocols share one bind address (`host`). Only the port differs per protocol. Env vars take precedence over the TOML file.
210+
All protocols share one bind address (`host`). Only the port differs per protocol.
106211

107212
```toml
108213
# nodedb.toml
@@ -133,19 +238,19 @@ ilp = false # Example: disable TLS for ILP ingest
133238

134239
**Server settings:**
135240

136-
| Config field | Environment variable | Default |
137-
| ------------------ | ------------------------- | ---------------- |
138-
| `host` | `NODEDB_HOST` | `127.0.0.1` |
139-
| `ports.native` | `NODEDB_PORT_NATIVE` | `6433` |
140-
| `ports.pgwire` | `NODEDB_PORT_PGWIRE` | `6432` |
141-
| `ports.http` | `NODEDB_PORT_HTTP` | `6480` |
142-
| `ports.resp` | `NODEDB_PORT_RESP` | disabled |
143-
| `ports.ilp` | `NODEDB_PORT_ILP` | disabled |
144-
| `data_dir` | `NODEDB_DATA_DIR` | `~/.nodedb/data` |
145-
| `memory_limit` | `NODEDB_MEMORY_LIMIT` | `1GiB` |
146-
| `data_plane_cores` | `NODEDB_DATA_PLANE_CORES` | CPUs - 1 |
147-
| `max_connections` | `NODEDB_MAX_CONNECTIONS` | `1024` |
148-
| `log_format` | `NODEDB_LOG_FORMAT` | `text` |
241+
| Config field | Environment variable | Default |
242+
| ------------------ | ------------------------- | ----------------------------------------------------- |
243+
| `host` | `NODEDB_HOST` | `127.0.0.1` |
244+
| `ports.native` | `NODEDB_PORT_NATIVE` | `6433` |
245+
| `ports.pgwire` | `NODEDB_PORT_PGWIRE` | `6432` |
246+
| `ports.http` | `NODEDB_PORT_HTTP` | `6480` |
247+
| `ports.resp` | `NODEDB_PORT_RESP` | disabled |
248+
| `ports.ilp` | `NODEDB_PORT_ILP` | disabled |
249+
| `data_dir` | `NODEDB_DATA_DIR` | `~/.nodedb/data` (binary), `/var/lib/nodedb` (Docker) |
250+
| `memory_limit` | `NODEDB_MEMORY_LIMIT` | `1GiB` |
251+
| `data_plane_cores` | `NODEDB_DATA_PLANE_CORES` | CPUs - 1 |
252+
| `max_connections` | `NODEDB_MAX_CONNECTIONS` | `1024` |
253+
| `log_format` | `NODEDB_LOG_FORMAT` | `text` |
149254

150255
**Per-protocol TLS** (only applies when `[server.tls]` is configured):
151256

0 commit comments

Comments
 (0)