7.0 KiB
Ginxsom Static MUSL Build Guide
This guide explains how to build and deploy Ginxsom as a fully static MUSL binary with zero runtime dependencies.
Overview
Ginxsom now supports building as a static MUSL binary using Alpine Linux and Docker. This produces a truly portable binary that works on any Linux distribution without requiring any system libraries.
Benefits
| Feature | Static MUSL | Dynamic glibc |
|---|---|---|
| Portability | ✓ Any Linux | ✗ Requires matching libs |
| Dependencies | None | libfcgi, libsqlite3, etc. |
| Deployment | Copy one file | Build on target |
| Binary Size | ~7-10 MB | ~2-3 MB + libraries |
| Deployment Time | ~10 seconds | ~5-10 minutes |
Prerequisites
- Docker installed and running
- Internet connection (for first build only)
- ~2GB disk space for Docker images
Quick Start
1. Build Static Binary
# Build production binary (optimized, stripped)
make static
# Or build debug binary (with symbols)
make static-debug
# Or use the script directly
./build_static.sh
./build_static.sh --debug
The binary will be created in build/ginxsom-fcgi_static_x86_64 (or _arm64 for ARM systems).
2. Verify Binary
# Check if truly static
ldd build/ginxsom-fcgi_static_x86_64
# Should output: "not a dynamic executable"
# Check file info
file build/ginxsom-fcgi_static_x86_64
# Should show: "statically linked"
# Check size
ls -lh build/ginxsom-fcgi_static_x86_64
3. Deploy to Server
# Use the simplified deployment script
./deploy_static.sh
# Or manually copy and start
scp build/ginxsom-fcgi_static_x86_64 user@server:/path/to/ginxsom/
ssh user@server
chmod +x /path/to/ginxsom/ginxsom-fcgi_static_x86_64
sudo spawn-fcgi -M 666 -u www-data -g www-data \
-s /tmp/ginxsom-fcgi.sock \
-- /path/to/ginxsom/ginxsom-fcgi_static_x86_64 \
--db-path /path/to/db/ginxsom.db \
--storage-dir /var/www/html/blossom
Build Process Details
What Happens During Build
-
Docker Image Creation (5-10 minutes first time, cached after):
- Uses Alpine Linux 3.19 (native MUSL)
- Builds secp256k1 statically
- Builds nostr_core_lib with required NIPs
- Embeds web interface files
- Compiles Ginxsom with full static linking
-
Binary Extraction:
- Extracts binary from Docker container
- Verifies static linking
- Makes executable
-
Verification:
- Checks for dynamic dependencies
- Reports file size
- Tests execution
Docker Layers (Cached)
The Dockerfile uses multi-stage builds with caching:
Layer 1: Alpine base + dependencies (cached)
Layer 2: Build secp256k1 (cached)
Layer 3: Initialize git submodules (cached unless .gitmodules changes)
Layer 4: Build nostr_core_lib (cached unless nostr_core_lib changes)
Layer 5: Embed web files (cached unless api/ changes)
Layer 6: Build Ginxsom (rebuilds when src/ changes)
This means subsequent builds are much faster (~1-2 minutes) since only changed layers rebuild.
Deployment Comparison
Old Dynamic Build Deployment
# 1. Sync entire project (30 seconds)
rsync -avz . user@server:/path/
# 2. Build on remote server (5-10 minutes)
ssh user@server "cd /path && make clean && make"
# 3. Restart service (10 seconds)
ssh user@server "sudo systemctl restart ginxsom"
# Total: ~6-11 minutes
New Static Build Deployment
# 1. Build locally once (5-10 minutes first time, cached after)
make static
# 2. Copy binary (10 seconds)
scp build/ginxsom-fcgi_static_x86_64 user@server:/path/
# 3. Restart service (10 seconds)
ssh user@server "sudo systemctl restart ginxsom"
# Total: ~20 seconds (after first build)
Cleanup
Automatic Cleanup
The static build script automatically cleans up old dynamic build artifacts (.o files and ginxsom-fcgi binary) after successfully building the static binary. This keeps your build/ directory clean.
Manual Cleanup
# Clean dynamic build artifacts (preserves static binaries)
make clean
# Clean everything including static binaries
make clean-all
# Or manually remove specific files
rm -f build/*.o
rm -f build/ginxsom-fcgi
rm -f build/ginxsom-fcgi_static_*
Troubleshooting
Docker Not Found
# Install Docker
sudo apt install docker.io
# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker
Build Fails
# Clean Docker cache and rebuild
docker system prune -a
make static
Binary Won't Run on Target
# Verify it's static
ldd build/ginxsom-fcgi_static_x86_64
# Check architecture matches
file build/ginxsom-fcgi_static_x86_64
uname -m # On target system
Alpine Package Not Found
If you get errors about missing Alpine packages, the package name may have changed. Check Alpine's package database:
Advanced Usage
Cross-Compilation
Build for different architectures:
# Build for ARM64 on x86_64 machine
docker build --platform linux/arm64 -f Dockerfile.alpine-musl -t ginxsom-arm64 .
Custom NIPs
Edit Dockerfile.alpine-musl line 66 to change which NIPs are included:
./build.sh --nips=1,6,19 # Minimal
./build.sh --nips=1,6,13,17,19,44,59 # Full (default)
Debug Build
# Build with debug symbols (no optimization)
make static-debug
# Binary will be larger but include debugging info
gdb build/ginxsom-fcgi_static_x86_64
File Structure
ginxsom/
├── Dockerfile.alpine-musl # Alpine Docker build definition
├── build_static.sh # Build script wrapper
├── deploy_static.sh # Simplified deployment script
├── Makefile # Updated with 'static' target
└── build/
└── ginxsom-fcgi_static_x86_64 # Output binary
CI/CD Integration
GitHub Actions Example
name: Build Static Binary
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Build static binary
run: make static
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: ginxsom-static
path: build/ginxsom-fcgi_static_x86_64
Performance
Static MUSL binaries have minimal performance impact:
| Metric | Static MUSL | Dynamic glibc |
|---|---|---|
| Startup Time | ~50ms | ~40ms |
| Memory Usage | Similar | Similar |
| Request Latency | Identical | Identical |
| Binary Size | 7-10 MB | 2-3 MB + libs |
The slight startup delay is negligible for a long-running FastCGI process.
References
Support
For issues with static builds:
- Check Docker is running:
docker info - Verify submodules:
git submodule status - Clean and rebuild:
docker system prune -a && make static - Check logs in Docker build output