portable server with embedded for musl has different feature

This commit is contained in:
Priec
2026-08-14 13:56:57 +02:00
parent da8457cd4e
commit 0e79c62511
6 changed files with 312 additions and 16 deletions

3
Cargo.lock generated
View File

@@ -6888,6 +6888,7 @@ dependencies = [
"datafusion-table-providers", "datafusion-table-providers",
"dotenvy", "dotenvy",
"email_address", "email_address",
"flate2",
"futures", "futures",
"gtin-validate", "gtin-validate",
"iban_validate", "iban_validate",
@@ -6919,6 +6920,8 @@ dependencies = [
"steel-decimal", "steel-decimal",
"steel-derive", "steel-derive",
"tantivy", "tantivy",
"tar",
"tempfile",
"thiserror 2.0.18", "thiserror 2.0.18",
"time", "time",
"tokio", "tokio",

View File

@@ -10,18 +10,19 @@
flake-utils.lib.eachDefaultSystem (system: flake-utils.lib.eachDefaultSystem (system:
let let
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
portableServer = import ./packaging/musl {
inherit pkgs;
source = self;
version = "0.8.41";
};
in in
{ {
packages = pkgs.lib.optionalAttrs (system == "x86_64-linux") {
portable-server = portableServer;
};
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ pkgs.openssl ];
pkgs.openssl
pkgs.krb5
pkgs.lz4
pkgs.zstd
pkgs.libxml2_13.out
pkgs.zlib
pkgs.readline
];
buildInputs = with pkgs; [ buildInputs = with pkgs; [
mermaid-cli mermaid-cli
@@ -51,12 +52,6 @@
# OpenSSL for crypto dependencies # OpenSSL for crypto dependencies
openssl openssl
openssl.dev openssl.dev
krb5
lz4
zstd
libxml2_13.out
zlib
readline
# PostgreSQL for sqlx # PostgreSQL for sqlx
postgresql postgresql

35
packaging/musl/README.md Normal file
View File

@@ -0,0 +1,35 @@
# Portable Linux server build
This build produces one `x86_64` Linux server executable with embedded
PostgreSQL. The Rust program is statically linked against musl. PostgreSQL is
compiled against musl without optional host-library dependencies and is stored
inside the executable by `postgresql_embedded`.
PostgreSQL itself remains dynamically loadable so the `btree_gist` extension
used by the migrations works. Its musl loader is included in the embedded
archive, and static launcher executables invoke every PostgreSQL tool through
that loader. Neither glibc, musl, PostgreSQL nor timezone data needs to be
installed on the target machine.
Build directly through the repository's Nix flake:
```sh
nix build '.?submodules=1#portable-server'
```
The `submodules=1` flag includes the workspace's nested Rust repositories. The
artifact is available as `result/bin/server`. The derivation initializes a
temporary cluster, creates `btree_gist`, sets the timezone to `UTC`, and checks
that the final Rust executable has no ELF interpreter.
The resulting executable is intended for 64-bit Linux kernels. Run it normally:
```sh
./result/bin/server server
```
Use the external PostgreSQL build on NixOS when preferred:
```sh
cargo build --release --package server --no-default-features
```

171
packaging/musl/default.nix Normal file
View File

@@ -0,0 +1,171 @@
{ pkgs, source, version }:
let
muslPkgs = pkgs.pkgsMusl;
postgresqlVersion = "17.10";
postgresqlArchiveVersion = "17.10.0";
archiveName = "postgresql-${postgresqlArchiveVersion}-x86_64-unknown-linux-musl.tar.gz";
bootstrapInput = pname: inputs:
pkgs.lib.findFirst
(input: (input.pname or "") == pname)
(throw "Nixpkgs does not expose ${pname} for the musl toolchain")
inputs;
bootstrapRustPlatform = muslPkgs.makeRustPlatform {
rustc = bootstrapInput
"rustc-bootstrap"
muslPkgs.rustc-unwrapped.nativeBuildInputs;
cargo = bootstrapInput
"cargo-bootstrap"
muslPkgs.cargo.nativeBuildInputs;
};
staticOpenSSL = muslPkgs.openssl.overrideAttrs (old: {
configureFlags =
builtins.filter (flag: flag != "shared") old.configureFlags
++ [ "no-shared" ];
doCheck = false;
});
postgresqlSource = pkgs.fetchurl {
url = "https://ftp.postgresql.org/pub/source/v${postgresqlVersion}/postgresql-${postgresqlVersion}.tar.bz2";
hash = "sha256-B4oDUW3NvbcF/sr0Feo9E6lWxYnkbwn+1ooG+wBZjJA=";
};
postgresqlArchive = muslPkgs.stdenv.mkDerivation {
pname = "komp-ac-postgresql-musl";
version = postgresqlArchiveVersion;
src = postgresqlSource;
nativeBuildInputs = [
pkgs.bison
pkgs.file
pkgs.flex
pkgs.gnutar
pkgs.gzip
pkgs.perl
];
configurePhase = ''
runHook preConfigure
./configure \
--prefix="$out/postgresql" \
--disable-nls \
--without-gssapi \
--without-icu \
--without-ldap \
--without-libxml \
--without-libxslt \
--without-llvm \
--without-lz4 \
--without-openssl \
--without-pam \
--without-readline \
--without-systemd \
--without-zlib \
--without-zstd
runHook postConfigure
'';
buildPhase = ''
runHook preBuild
make -j"$NIX_BUILD_CORES"
make -C contrib/btree_gist -j"$NIX_BUILD_CORES"
runHook postBuild
'';
installPhase = ''
runHook preInstall
make install-strip
make -C contrib/btree_gist install
"$CC" -static -Os -s -Wall -Wextra -Werror \
-o postgresql-launcher \
${./postgresql-launcher.c}
mkdir -p "$out/postgresql/libexec"
for tool in "$out"/postgresql/bin/*; do
toolName="''${tool##*/}"
mv "$tool" "$out/postgresql/libexec/$toolName"
cp postgresql-launcher "$out/postgresql/bin/$toolName"
done
cp ${muslPkgs.musl}/lib/ld-musl-x86_64.so.1 \
"$out/postgresql/lib/ld-musl-x86_64.so.1"
file "$out/postgresql/bin/postgres" | grep -q 'statically linked'
"$out/postgresql/bin/postgres" --version
mkdir postgresql-data postgresql-socket
"$out/postgresql/bin/initdb" \
--auth=trust \
--no-locale \
-D "$PWD/postgresql-data"
"$out/postgresql/bin/pg_ctl" \
-D "$PWD/postgresql-data" \
-l "$PWD/postgresql.log" \
-o "-k $PWD/postgresql-socket -p 55432" \
-w start
"$out/postgresql/bin/psql" \
-h "$PWD/postgresql-socket" \
-p 55432 \
-d postgres \
-c "CREATE EXTENSION btree_gist; SET TIME ZONE 'UTC';"
"$out/postgresql/bin/pg_ctl" \
-D "$PWD/postgresql-data" \
-m fast \
-w stop
tar -C "$out" -czf "$out/${archiveName}" postgresql
runHook postInstall
'';
dontPatchELF = true;
dontStrip = true;
};
in
bootstrapRustPlatform.buildRustPackage {
pname = "komp-ac-server-portable";
inherit version source;
src = source;
cargoLock.lockFile = ../../Cargo.lock;
cargoBuildFlags = [
"--package"
"server"
"--no-default-features"
"--features"
"portable-postgres"
];
auditable = false;
doCheck = false;
nativeBuildInputs = [
pkgs.binutils
pkgs.cmake
pkgs.file
pkgs.pkg-config
pkgs.protobuf
];
buildInputs = [ staticOpenSSL ];
OPENSSL_STATIC = "1";
OPENSSL_LIB_DIR = "${staticOpenSSL.out}/lib";
OPENSSL_INCLUDE_DIR = "${staticOpenSSL.dev}/include";
POSTGRESQL_VERSION = "=${postgresqlArchiveVersion}";
KOMP_AC_POSTGRESQL_ARCHIVE = "${postgresqlArchive}/${archiveName}";
KOMP_AC_POSTGRESQL_VERSION = postgresqlArchiveVersion;
SQLX_OFFLINE = "true";
RUSTFLAGS = "-Ctarget-feature=+crt-static";
postInstall = ''
file "$out/bin/server" | grep -q 'statically linked'
! readelf -l "$out/bin/server" | grep -q 'Requesting program interpreter'
'';
meta = {
description = "Portable komp_ac server with embedded musl PostgreSQL";
license = pkgs.lib.licenses.agpl3Plus;
platforms = [ "x86_64-linux" ];
mainProgram = "server";
};
}

View File

@@ -0,0 +1,92 @@
#define _GNU_SOURCE
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void fail(const char *message)
{
fprintf(stderr, "embedded PostgreSQL launcher: %s\n", message);
exit(127);
}
static void join_path(char *destination, size_t capacity, const char *left, const char *right)
{
int length = snprintf(destination, capacity, "%s/%s", left, right);
if (length < 0 || (size_t) length >= capacity)
fail("installation path is too long");
}
int main(int argc, char **argv)
{
char executable[PATH_MAX];
ssize_t executable_length = readlink("/proc/self/exe", executable, sizeof(executable) - 1);
if (executable_length < 0) {
perror("embedded PostgreSQL launcher: /proc/self/exe");
return 127;
}
executable[executable_length] = '\0';
char *tool = strrchr(executable, '/');
if (tool == NULL || tool[1] == '\0')
fail("cannot determine the requested PostgreSQL tool");
tool++;
char root[PATH_MAX];
if ((size_t) executable_length >= sizeof(root))
fail("installation path is too long");
memcpy(root, executable, (size_t) executable_length + 1);
char *bin_separator = strrchr(root, '/');
if (bin_separator == NULL)
fail("cannot determine the PostgreSQL installation directory");
*bin_separator = '\0';
char *root_separator = strrchr(root, '/');
if (root_separator == NULL)
fail("cannot determine the PostgreSQL installation directory");
*root_separator = '\0';
char loader[PATH_MAX];
char library_path[PATH_MAX];
char real_tool[PATH_MAX];
join_path(loader, sizeof(loader), root, "lib/ld-musl-x86_64.so.1");
join_path(library_path, sizeof(library_path), root, "lib");
char real_tool_relative[PATH_MAX];
int relative_length = snprintf(
real_tool_relative,
sizeof(real_tool_relative),
"libexec/%s",
tool
);
if (relative_length < 0 || (size_t) relative_length >= sizeof(real_tool_relative))
fail("tool path is too long");
join_path(real_tool, sizeof(real_tool), root, real_tool_relative);
char **loader_argv = calloc((size_t) argc + 6, sizeof(char *));
if (loader_argv == NULL) {
perror("embedded PostgreSQL launcher: calloc");
return 127;
}
loader_argv[0] = loader;
loader_argv[1] = "--library-path";
loader_argv[2] = library_path;
loader_argv[3] = "--argv0";
loader_argv[4] = executable;
loader_argv[5] = real_tool;
for (int index = 1; index < argc; index++)
loader_argv[index + 5] = argv[index];
execv(loader, loader_argv);
fprintf(
stderr,
"embedded PostgreSQL launcher: cannot execute %s: %s\n",
real_tool,
strerror(errno)
);
return 127;
}

2
server

Submodule server updated: b79e9a0b57...440e0cf0dc