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

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;
}