Skip to main content

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.
Dynamic plugins only work on Linux and macOS (Darwin). Windows is not supported by Go’s plugin system.

Static vs Dynamic Builds

Static Builds (Default)

Bifrost’s default build configuration creates statically linked binaries:
Characteristics:
  • ✅ Portable across all Linux distributions (musl, glibc, etc.)
  • ✅ No external dependencies required at runtime
  • ✅ Smaller deployment surface area
  • Cannot load Go plugins
Use static builds when: You don’t need custom plugins and want maximum portability.

Dynamic Builds (For Plugins)

To enable plugin support, build without static linking flags:
Characteristics:
  • Can load Go plugins (.so files)
  • ✅ Slightly faster compilation
  • ⚠️ Must match the target system’s libc (musl vs glibc)
  • ⚠️ Less portable across different Linux distributions
Use dynamic builds when: You need custom plugin support.

Building with Makefile

The easiest way to build a dynamic binary is using the DYNAMIC=1 flag with the Makefile:

Local Build

This creates tmp/bifrost-http as a dynamically linked binary.

Cross-Compilation

How It Works

The DYNAMIC=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.
Key changes from static build:
  • 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
Build and run:

Option B: Debian (glibc)

Use this for Debian/Ubuntu-based deployments or when deploying to glibc-based systems.
Key differences from Alpine version:
  • Uses bookworm (Debian 12) base images instead of Alpine
  • Installs apt packages instead of apk
  • Runtime uses glibc (libc6) instead of musl
  • Uses useradd instead of adduser for user creation
Build and run:

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

  • A binary built with musl will NOT run on glibc systems.
  • A binary built with glibc will NOT run on musl systems.
  • Plugins and Bifrost MUST use the same libc.

Why This Matters

When you build a dynamic binary:
The binary is linked to a specific libc implementation. If you try to run it on a system with a different libc, you’ll get errors like:

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:
See the hello-world plugin Makefile for a complete example.

Verification

Verify Dynamic Linking

After building, check that your binary is dynamically linked:
If you see 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, your go.mod should match Bifrost’s Go version:
Import only the Bifrost modules you need. Most plugins only require core. Use framework if you need access to config stores, vector stores, or other framework features.

Troubleshooting

Common Errors

1. Cannot load plugin - Go version mismatch

Cause: Plugin and Bifrost were built with different Go versions. Solution: Use the exact same Go version (Go 1.26.1) for both:
Refer to Go Version and Package Compatibility for details.

2. Shared library not found

Cause: Binary built with musl trying to run on glibc system (or vice versa). Solution: Rebuild with the correct libc for your target system.

3. Plugin architecture mismatch

Cause: Plugin and Bifrost built for different architectures (amd64 vs arm64). Solution: Ensure GOARCH matches for both builds:

4. Plugin file not found

Cause: Plugin file path is incorrect in config. Solution: Use absolute paths or verify relative paths:

Best Practices

1. Document Your Build Environment

Create a BUILD.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:
This ensures plugins and Bifrost use identical build environments.

Next Steps

Now that you have a dynamically linked Bifrost binary:
  1. Write your first plugin - Learn the plugin API and create custom functionality
  2. Deploy with plugins - Best practices for production deployments
  3. Example plugins - Study working examples
For questions or issues with dynamic builds and plugins, visit our GitHub Discussions or Discord community.