From 4648de9e62911432fef21a56241badda4a80f97c Mon Sep 17 00:00:00 2001 From: houseme Date: Wed, 27 May 2026 22:47:47 +0800 Subject: [PATCH] chore(deploy): refine systemd and nixos service docs (#3096) * fix(deploy): simplify systemd service startup * docs(deploy): add nixos systemd service example * docs(deploy): add nixos service guide * chore(deps): update pulsar and pyroscope * docs(deploy): refine nixos service guidance * fix(deploy): use explicit rustfs server entrypoint --- Cargo.lock | 94 +++++++-------------- Cargo.toml | 4 +- deploy/build/rustfs.nixos.md | 112 ++++++++++++++++++++++++++ deploy/build/rustfs.nixos.service.nix | 107 ++++++++++++++++++++++++ deploy/build/rustfs.service | 20 ++--- 5 files changed, 257 insertions(+), 80 deletions(-) create mode 100644 deploy/build/rustfs.nixos.md create mode 100644 deploy/build/rustfs.nixos.service.nix diff --git a/Cargo.lock b/Cargo.lock index 7cefb518e..55cda1201 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,7 +255,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -266,7 +266,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3570,7 +3570,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3585,9 +3585,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -3906,7 +3906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4079,20 +4079,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "framehop" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a5a3f0acb82df800ca3aa50c0d60d286c5d13d4cfc3114b3a9663f13b032fe" -dependencies = [ - "arrayvec", - "cfg-if", - "fallible-iterator 0.3.0", - "gimli 0.31.1", - "macho-unwind-info", - "pe-unwind-info 0.4.0", -] - [[package]] name = "framehop" version = "0.16.0" @@ -4104,7 +4090,7 @@ dependencies = [ "fallible-iterator 0.3.0", "gimli 0.33.0", "macho-unwind-info", - "pe-unwind-info 0.6.0", + "pe-unwind-info", ] [[package]] @@ -4311,16 +4297,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -dependencies = [ - "fallible-iterator 0.3.0", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.32.3" @@ -5344,7 +5320,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6045,9 +6021,9 @@ checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "memmap2" @@ -6567,7 +6543,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6739,7 +6715,7 @@ version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "chrono", "getrandom 0.2.17", "http 1.4.1", @@ -7367,19 +7343,6 @@ dependencies = [ "hmac 0.13.0", ] -[[package]] -name = "pe-unwind-info" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500fa4cdeacd98997c5865e3d0d1cb8fe7e9d7d75ecc775e07989a433a9a9a59" -dependencies = [ - "arrayvec", - "bitflags 2.11.1", - "thiserror 2.0.18", - "zerocopy", - "zerocopy-derive", -] - [[package]] name = "pe-unwind-info" version = "0.6.0" @@ -7768,7 +7731,7 @@ dependencies = [ "backtrace", "cfg-if", "findshlibs", - "framehop 0.16.0", + "framehop", "inferno 0.11.21", "libc", "log", @@ -7972,7 +7935,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck", - "itertools 0.14.0", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -7992,7 +7955,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" dependencies = [ "heck", - "itertools 0.14.0", + "itertools 0.10.5", "log", "multimap", "petgraph 0.8.3", @@ -8013,7 +7976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -8026,7 +7989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -8143,9 +8106,9 @@ dependencies = [ [[package]] name = "pulsar" -version = "6.7.2" +version = "6.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2367cb38f1b65857bc11dd13b2adf13b7a1d991ef1cd43572f1420958c56cc2" +checksum = "a2784d3ed4cc69abf1257842f40b5ca89cfa2e6047912a9691193532c211ab13" dependencies = [ "async-channel", "async-trait", @@ -8173,11 +8136,10 @@ dependencies = [ [[package]] name = "pyroscope" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942d4561e34d24ce01820f6e6bdf1dcff292b6f0c03245b0b4e5b4f89dddd85d" +checksum = "01ebd83db08e07a69b4291e7043f5f2ca964c8869e0bd0b6d09b8b062de775bd" dependencies = [ - "framehop 0.13.3", "lazy_static", "libc", "libflate", @@ -10223,7 +10185,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -10297,7 +10259,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -11055,7 +11017,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11196,7 +11158,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11465,10 +11427,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -12646,7 +12608,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9f896f562..529395647 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,7 +127,7 @@ futures = "0.3.32" futures-core = "0.3.32" futures-util = "0.3.32" pollster = "0.4.0" -pulsar = { version = "6.7.2", default-features = false, features = ["tokio-rustls-runtime"] } +pulsar = { version = "6.8.0", default-features = false, features = ["tokio-rustls-runtime"] } lapin = { version = "4.10.0", default-features = false, features = ["tokio", "rustls", "rustls--aws_lc_rs"] } hyper = { version = "1.9.0", features = ["http2", "http1", "server"] } hyper-rustls = { version = "0.27.9", default-features = false, features = ["native-tokio", "http1", "tls12", "logging", "http2", "aws-lc-rs", "webpki-roots"] } @@ -301,7 +301,7 @@ opentelemetry-otlp = { version = "0.32.0", features = ["gzip-http", "reqwest-rus opentelemetry_sdk = { version = "0.32.1", features = ["rt-tokio"] } opentelemetry-semantic-conventions = { version = "0.32.0", features = ["semconv_experimental"] } opentelemetry-stdout = { version = "0.32.0" } -pyroscope = { version = "2.0.4", features = ["backend-pprof-rs"] } +pyroscope = { version = "2.0.5", features = ["backend-pprof-rs"] } # FTP and SFTP libunftp = { version = "0.23.0", features = ["experimental"] } diff --git a/deploy/build/rustfs.nixos.md b/deploy/build/rustfs.nixos.md new file mode 100644 index 000000000..baa601823 --- /dev/null +++ b/deploy/build/rustfs.nixos.md @@ -0,0 +1,112 @@ +# RustFS NixOS Service Guide + +This guide explains how to use [rustfs.nixos.service.nix](./rustfs.nixos.service.nix) as a starting point for managing RustFS with `systemd` on NixOS. + +## 1. What the example does + +The NixOS example is designed for the current RustFS startup model: + +- `Type = "notify"` so `systemd` waits for RustFS to publish `READY=1` +- a dedicated `rustfs` system user instead of running the service as `root` +- `WorkingDirectory = "/var/lib/rustfs"` instead of using a log directory as the working directory +- `KillMode = "control-group"` and `SendSIGKILL = true` so stuck shutdowns do not leave the unit hanging forever +- `StandardOutput = "journal"` and `StandardError = "journal"` so logs stay in `journald` +- runtime configuration through `RUSTFS_*` environment variables +- secret file wiring through `RUSTFS_ACCESS_KEY_FILE` and `RUSTFS_SECRET_KEY_FILE` + +## 2. Import the example into your NixOS configuration + +You can either copy the contents into your own module, or import it directly and provide `rustfsPkg`. + +Example: + +```nix +{ + imports = [ + ./deploy/build/rustfs.nixos.service.nix + ]; + + _module.args.rustfsPkg = pkgs.callPackage ./path/to/rustfs/package.nix { }; +} +``` + +If you already package RustFS through a flake output or overlay, point `rustfsPkg` at that package instead. + +## 3. Adjust the default paths + +The example uses these defaults: + +- data directory: `/srv/rustfs` +- state directory: `/var/lib/rustfs` +- log directory: `/var/log/rustfs` + +Before enabling the service, make sure your RustFS volumes and any TLS material match your real deployment layout. + +The example currently sets: + +```nix +RUSTFS_VOLUMES = "/srv/rustfs/vol{1...4}"; +RUSTFS_ADDRESS = "0.0.0.0:9000"; +RUSTFS_CONSOLE_ENABLE = "true"; +RUSTFS_CONSOLE_ADDRESS = "0.0.0.0:9001"; +``` + +Update these values to match your storage topology and exposure policy. + +## 4. Wire secrets through runtime files + +Do not put credentials directly into `environment` if they would be stored in the Nix store. + +RustFS supports file-based secrets, so on NixOS the preferred pattern is: + +```nix +systemd.services.rustfs.environment = { + RUSTFS_ACCESS_KEY_FILE = config.age.secrets.rustfs-access.path; + RUSTFS_SECRET_KEY_FILE = config.age.secrets.rustfs-secret.path; +}; +``` + +The same shape also works with `sops-nix`, `agenix`, or any other runtime secret manager that exposes files under `/run`. + +## 5. Rebuild and enable the service + +After integrating the unit into your NixOS configuration: + +```bash +sudo nixos-rebuild switch +sudo systemctl enable --now rustfs +``` + +## 6. Verify readiness and logs + +Check whether `systemd` sees the service as fully ready: + +```bash +systemctl status rustfs +systemctl show rustfs -p Type -p ActiveState -p SubState +``` + +Follow the service logs: + +```bash +journalctl -u rustfs -f +``` + +Look for the point where RustFS reports that startup is complete and `systemd` transitions the unit into the running state. + +## 7. Common NixOS-specific notes + +- If you previously used `SendSIGKILL = false`, expect shutdown behavior to change. The example favors reliable service recovery over indefinite graceful waits. +- If your service really needs more startup time, increase `TimeoutStartSec`, but keep it bounded. Very large values can hide broken startup states. +- If you must write file logs instead of journald logs, change `StandardOutput` and `StandardError`, then make sure the target path is writable by the `rustfs` user. +- If you decide to run as `root`, review the hardening flags again. The example is written for a dedicated unprivileged service user. + +## 8. Validation note + +If your deployment depends on this example directly, validate the final module in your own NixOS environment before rollout. A common check is: + +```bash +nix-instantiate --parse /path/to/your/module.nix +``` + +If you integrate the example into a host configuration or flake, prefer validating it through your normal `nixos-rebuild`, flake check, or CI pipeline as well. diff --git a/deploy/build/rustfs.nixos.service.nix b/deploy/build/rustfs.nixos.service.nix new file mode 100644 index 000000000..c4972f3ef --- /dev/null +++ b/deploy/build/rustfs.nixos.service.nix @@ -0,0 +1,107 @@ +{ + config, + lib, + pkgs, + rustfsPkg, + ... +}: +let + dataDir = "/srv/rustfs"; + stateDir = "/var/lib/rustfs"; + logDir = "/var/log/rustfs"; +in +{ + users.groups.rustfs = { }; + + users.users.rustfs = { + isSystemUser = true; + group = "rustfs"; + home = stateDir; + createHome = false; + description = "RustFS service user"; + }; + + systemd.services.rustfs = { + description = "RustFS Object Storage Server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + + # Keep non-secret runtime settings in the unit environment. For secrets, + # prefer runtime files so credentials do not land in the Nix store. + environment = { + RUSTFS_ADDRESS = "0.0.0.0:9000"; + RUSTFS_CONSOLE_ENABLE = "true"; + RUSTFS_CONSOLE_ADDRESS = "0.0.0.0:9001"; + RUSTFS_VOLUMES = "${dataDir}/vol{1...4}"; + }; + + serviceConfig = { + Type = "notify"; + NotifyAccess = "main"; + + User = "rustfs"; + Group = "rustfs"; + + # Use a state directory instead of a log directory as the cwd so + # relative paths do not accidentally resolve under /var/log. + WorkingDirectory = stateDir; + StateDirectory = "rustfs"; + LogsDirectory = "rustfs"; + + # Explicitly use the server entrypoint instead of relying on legacy + # CLI compatibility shims in service management. + ExecStart = "${rustfsPkg}/bin/rustfs server"; + + LimitNOFILE = 1048576; + LimitNPROC = 32768; + TasksMax = "infinity"; + + Restart = "on-failure"; + RestartSec = "10s"; + StartLimitIntervalSec = "5min"; + StartLimitBurst = 5; + + # RustFS publishes READY=1 after runtime readiness succeeds. These + # values leave room for cold starts without hiding permanently wedged + # instances for too long. + TimeoutStartSec = "5min"; + TimeoutStopSec = "45s"; + KillMode = "control-group"; + SendSIGKILL = true; + + # Keep the example neutral by default. If your deployment explicitly + # wants RustFS to outlive other workloads during OOM, tune this in your + # local NixOS configuration. + OOMScoreAdjust = 0; + + NoNewPrivileges = true; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "strict"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + UMask = "0077"; + + ReadWritePaths = [ + dataDir + stateDir + logDir + ]; + + StandardOutput = "journal"; + StandardError = "journal"; + }; + }; + + # Example secret wiring for sops-nix / agenix style runtime files: + # + # systemd.services.rustfs.environment = { + # RUSTFS_ACCESS_KEY_FILE = config.age.secrets.rustfs-access.path; + # RUSTFS_SECRET_KEY_FILE = config.age.secrets.rustfs-secret.path; + # }; +} diff --git a/deploy/build/rustfs.service b/deploy/build/rustfs.service index 4482edacb..d0d902e42 100644 --- a/deploy/build/rustfs.service +++ b/deploy/build/rustfs.service @@ -16,17 +16,11 @@ Group=rustfs # working directory WorkingDirectory=/opt/rustfs -# environment variable configuration and main program (Option 1: Directly specify arguments) -# Credentials are loaded from /etc/default/rustfs below. Replace the sample values before deployment. -ExecStart=/usr/local/bin/rustfs \ - --address 0.0.0.0:9000 \ - --volumes /data/rustfs/vol1,/data/rustfs/vol2 \ - --console-enable - -# environment variable configuration (Option 2: Use environment variables) -# rustfs example file see: `../config/rustfs.env` +# Environment-driven startup. +# rustfs reads address, volumes, console, credentials, and other runtime settings +# from RUSTFS_* variables. See `../config/rustfs.env` for an example template. EnvironmentFile=/etc/default/rustfs -ExecStart=/usr/local/bin/rustfs $RUSTFS_VOLUMES +ExecStart=/usr/local/bin/rustfs server # service log configuration LogsDirectory=rustfs @@ -43,8 +37,10 @@ Restart=always RestartSec=10s # graceful exit configuration -TimeoutStartSec=30s -TimeoutStopSec=30s +# Type=notify waits for READY=1, so startup timeout must cover RustFS initialization +# plus runtime readiness checks on slower disks or cold starts. +TimeoutStartSec=120s +TimeoutStopSec=45s # security settings NoNewPrivileges=true