Why Dynamic Linking?
Go’s plugin system requires dynamic linking to load.so files at runtime. By default, Bifrost builds are statically linked for maximum portability across Linux distributions - they bundle all dependencies including the C standard library (libc). However, statically linked binaries cannot load Go plugins.
To use custom plugins with Bifrost, you must build a dynamically linked binary that links against the system’s libc at runtime.
Static vs Dynamic Builds
Static Builds (Default)
Bifrost’s default build configuration creates statically linked binaries:- ✅ Portable across all Linux distributions (musl, glibc, etc.)
- ✅ No external dependencies required at runtime
- ✅ Smaller deployment surface area
- ❌ Cannot load Go plugins
Dynamic Builds (For Plugins)
To enable plugin support, build without static linking flags:- ✅ Can load Go plugins (
.sofiles) - ✅ Slightly faster compilation
- ⚠️ Must match the target system’s libc (musl vs glibc)
- ⚠️ Less portable across different Linux distributions
Building with Makefile
The easiest way to build a dynamic binary is using theDYNAMIC=1 flag with the Makefile:
Local Build
tmp/bifrost-http as a dynamically linked binary.
Cross-Compilation
How It Works
TheDYNAMIC=1 flag automatically:
- ✅ Removes
-extldflags "-static"from ldflags - ✅ Removes
-tags "sqlite_static"build tag - ✅ Keeps
CGO_ENABLED=1(required for SQLite and plugins) - ✅ Uses Docker for cross-compilation when needed
Building with Docker
For containerized deployments, you’ll need to modify the Dockerfile. Here are two complete examples based on your target environment’s libc.Option A: Alpine Linux (musl libc)
Use this for Alpine-based deployments or when you want minimal image size.Complete Dockerfile for Alpine (musl libc)
Complete Dockerfile for Alpine (musl libc)
- Line 40-44: Removed
-extldflags '-static'and-tags "sqlite_static" - Removed UPX compression step (optional, but simpler)
- Runtime uses musl libc from Alpine base image
Option B: Debian (glibc)
Use this for Debian/Ubuntu-based deployments or when deploying to glibc-based systems.Complete Dockerfile for Debian (glibc)
Complete Dockerfile for Debian (glibc)
- Uses
bookworm(Debian 12) base images instead of Alpine - Installs
aptpackages instead ofapk - Runtime uses glibc (libc6) instead of musl
- Uses
useraddinstead ofadduserfor user creation
libc Compatibility
Understanding libc (C standard library) compatibility is critical when building dynamic binaries and plugins.musl vs glibc
Linux distributions use one of two main C standard libraries:The Golden Rule
Why This Matters
When you build a dynamic binary:Choosing Your Build Environment
Decision Matrix:
Simple rule: Build with the same base OS family as your deployment target.
Building Plugins
Plugins must be built with the exact same environment as your Bifrost binary:Verification
Verify Dynamic Linking
After building, check that your binary is dynamically linked:statically linked, the binary will not load plugins.
Verify Plugin Compatibility
Test that your plugin loads successfully:Go Version and Package Compatibility
Go Version Requirement
Bifrost is built with Go 1.26.1. Your plugin must be compiled with the exact same Go version to ensure compatibility.Key Package Versions
Bifrost uses the following key packages across its three main modules that may affect plugin development:Transport Layer (transports/go.mod)
Core Layer (core/go.mod)
Framework Layer (framework/go.mod)
If your plugin imports any of these packages, use compatible versions to avoid runtime issues. Check
transports/go.mod, core/go.mod, and framework/go.mod for complete dependency lists.Checking Bifrost’s Dependencies
To see all dependencies used by Bifrost across its three main modules:Plugin go.mod Example
When creating a plugin, yourgo.mod should match Bifrost’s Go version:
Troubleshooting
Common Errors
1. Cannot load plugin - Go version mismatch
2. Shared library not found
3. Plugin architecture mismatch
GOARCH matches for both builds:
4. Plugin file not found
Best Practices
1. Document Your Build Environment
Create aBUILD.md file documenting:
- Go version used
- Base image (Alpine vs Debian)
- Build commands
- Target deployment platform
2. Use Consistent Tooling
Match Bifrost’s exact Go version and key dependencies (see Go Version and Package Compatibility):3. Test Plugin Loading Locally
Before deploying, test plugin loading:4. Version Your Plugins
Tag plugin builds with version and build info:5. Multi-Stage Dockerfiles for Plugins
Build plugins in the same Dockerfile as Bifrost:Next Steps
Now that you have a dynamically linked Bifrost binary:- Write your first plugin - Learn the plugin API and create custom functionality
- Deploy with plugins - Best practices for production deployments
- Example plugins - Study working examples
For questions or issues with dynamic builds and plugins, visit our GitHub Discussions or Discord community.

