caddymongodb

package module
v2.1.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 9, 2026 License: MIT Imports: 23 Imported by: 0

README

MongoDB Storage module for Caddy / Certmagic

MongoDB storage for CertMagic/Caddy TLS data with advanced caching and connection pooling. High-throughput, production-ready backend with Ristretto cache & bulk writes

Features

  • MongoDB Backend: Persists Caddy's TLS certificates and related CertMagic data in MongoDB.
  • CertMagic Compatible: Implements certmagic.Storage and certmagic.Locker interfaces.
  • High-Performance Caching: Utilizes Ristretto for in-memory caching of frequently accessed data.
    • Configurable Time-To-Live (TTL) for cache entries.
    • Configurable maximum number of cache entries.
  • Optimized Connection Pooling: Leverages the official MongoDB driver's connection pooling.
    • Configurable maximum and minimum pool sizes.
    • Configurable maximum connection idle time.
  • Efficient Bulk Writes: Optionally groups multiple Store operations into batches for improved write throughput.
    • Configurable maximum operations per batch.
    • Configurable flush interval for batching.
  • Singleflight Load Operations: Prevents thundering herd by ensuring that for a given key, only one request to the database is made if the item is not in the cache.
  • Robust Distributed Locking: Implements a locking mechanism suitable for distributed environments.
    • Utilizes MongoDB TTL indexes for automatic expiration of stale locks.
    • Periodic refresh of active locks.
    • Retry mechanism for lock acquisition.
  • Flexible Configuration:
    • Caddyfile directives.
    • JSON configuration.
    • Environment variable fallbacks for key parameters.
  • Comprehensive Key Listing: Supports both recursive and non-recursive listing of stored items.
  • Configurable Timeouts: Global timeout setting for MongoDB operations.
  • Structured Logging: Integrated with Zap logger for detailed operational and debug logging.
  • Automatic Index Management: Ensures creation of necessary MongoDB indexes for the locks collection to optimize performance and TTL functionality.

Configuration

Enable MongoDB storage for Caddy by specifying the module configuration in the Caddyfile:

{
    storage mongodb {
        uri "mongodb://localhost:27017"
        database "caddy"
        collection "certificates"
        timeout "10s"
        cache_ttl "10m"
        max_cache_entries 1000
        max_pool_size 100 # Optional: Max MongoDB connections
        min_pool_size 10  # Optional: Min MongoDB connections
        max_conn_idle_time 5m # Optional: Max idle time for a connection
    }
}
Configuration Options
Option Description Default Required
uri MongoDB connection string - Yes
database Database name - Yes
collection Collection name - Yes
locks_collection Locks Collection name locks No
timeout Operation timeout 10s No
cache_ttl Cache entry lifetime 10m No
max_cache_entries Maximum number of cached items 1000 No
max_pool_size Max connections in pool 100 (driver default) No
min_pool_size Min connections in pool 0 (driver default) No
max_conn_idle_time Max connection idle time 0 (driver default, no limit) No
enable_bulk_writes Group Store() calls false No
bulk_max_ops Max ops per bulk batch 100 No
bulk_flush_interval Flush window for bulk writes 500ms No
  • NOTE: if you operate a replica set you can keep sweep reads off the primary simply by appending readPreference=secondaryPreferred to the uri.

JSON Configuration

{
  "storage": {
    "module": "mongodb",
    "uri": "mongodb://root:example@mongo:27017/?compressors=zstd,snappy",
    "database": "caddy",
    "collection": "certs",
    "locks_collection": "locks",
    "timeout": "10s",

    "cache_ttl": "10m",
    "max_cache_entries": 1000,

    "max_pool_size": 100,
    "enable_bulk_writes": true,
    "bulk_max_ops": 100,
    "bulk_flush_interval": "500ms"
  }
}

Environment Variables

You can also configure the storage module using environment variables:

Variable Purpose
MONGODB_URI Connection string
MONGODB_DATABASE Database name
MONGODB_COLLECTION Cert collection
MONGODB_LOCKS_COLLECTION Lock collection (optional)
MONGODB_TIMEOUT Operation timeout (defaults to 10s if not set/invalid)
MONGODB_MAX_POOL_SIZE Max connections
MONGODB_MIN_POOL_SIZE Min connections
MONGODB_MAX_CONN_IDLE_TIME Max idle time (driver default is no limit if not set)

Building with xcaddy

To build Caddy with the MongoDB storage module:

xcaddy build \
    --with github.com/root-sector/caddy-storage-mongodb/v2@v2.1.6

For v2.x releases, Go semantic import versioning requires the module path to include /v2 in both go.mod and the xcaddy --with argument. Create release tags only after the tagged commit declares module github.com/root-sector/caddy-storage-mongodb/v2; otherwise go get and xcaddy will reject the tag.

Docker

Production Dockerfile
# Version to build
ARG CADDY_VERSION="2.11.4"

# Build stage
FROM caddy:${CADDY_VERSION}-builder AS builder

# Add module with xcaddy
RUN xcaddy build \
    --with github.com/root-sector/caddy-storage-mongodb/v2@v2.1.6

# Final stage
FROM caddy:${CADDY_VERSION}

# Copy the built Caddy binary
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

# Copy the Caddyfile
COPY Caddyfile /etc/caddy/Caddyfile

# Format the Caddyfile
RUN caddy fmt --overwrite /etc/caddy/Caddyfile
Development Dockerfile
# Version to build
ARG CADDY_VERSION="2.11.4"

# Build stage
FROM caddy:${CADDY_VERSION}-builder AS builder

# Add module with xcaddy
COPY caddy-storage-mongodb /caddy-storage-mongodb
RUN xcaddy build \
    --with github.com/root-sector/caddy-storage-mongodb/v2=/caddy-storage-mongodb

# Final stage
FROM caddy:${CADDY_VERSION}

# Copy the built Caddy binary
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

# Copy the Caddyfile
COPY Caddyfile /etc/caddy/Caddyfile

# Format the Caddyfile
RUN caddy fmt --overwrite /etc/caddy/Caddyfile

Testing

To run the tests, first start a test MongoDB instance:

docker-compose up -d mongodb

Then run the tests:

go test -v ./...

Benchmarking

The module includes benchmark tests to evaluate the performance of core storage operations. These benchmarks use testcontainers-go to spin up an isolated MongoDB instance for each run, ensuring consistent and reliable results.

Prerequisites
  • Docker: Ensure Docker is installed and running on your system, as testcontainers-go relies on it.
Running Benchmarks

Navigate to the module directory (caddy-storage-mongodb) and use the standard go test command with the -bench flag. To avoid running regular tests, you can use -run="^$".

To achieve cleaner benchmark output (suppressing INFO and DEBUG logs from the storage module itself), you can set the CADDY_MONGODB_BENCHMARK_LOG_LEVEL environment variable to error or panic before running the benchmarks. This feature requires the version of the module that includes this environment variable check.

Windows (PowerShell):

$env:CADDY_MONGODB_BENCHMARK_LOG_LEVEL="error"
go test -run="^$" -bench="."
# To clear after: Remove-Item Env:CADDY_MONGODB_BENCHMARK_LOG_LEVEL

Linux/macOS (bash):

CADDY_MONGODB_BENCHMARK_LOG_LEVEL=error go test -run="^$" -bench="."

Common Benchmark Commands:

# Run all benchmarks in the package
# On Windows, ensure the dot for "all benchmarks" is quoted.
go test -run="^$" -bench="."

# Run a specific benchmark function (e.g., BenchmarkStore)
go test -run="^$" -bench="^BenchmarkStore$"

# Run benchmarks with memory allocation statistics
go test -run="^$" -bench="." -benchmem

# Run benchmarks multiple times (e.g., 5 times) for more stable results
# Adding -v can show b.Log output from your benchmarks, useful for seeing setup steps.
go test -run="^$" -bench="." -count=5 -v

# Run benchmarks for a specific duration (e.g., 3 seconds per benchmark)
go test -run="^$" -bench="." -benchtime=3s
Example Benchmark Output

Here is a sample output from running go test -run="^$" -bench="." on a Windows machine with a 12th Gen Intel i7 CPU:

goos: windows
goarch: amd64
pkg: github.com/root-sector/caddy-storage-mongodb/v2
cpu: 12th Gen Intel(R) Core(TM) i7-12700KF
BenchmarkStore/WithBulkWrites-20                   16597             64680 ns/op
BenchmarkStore/WithoutBulkWrites-20                 2736            422473 ns/op
BenchmarkLoad/CacheHit-20                           5421            317896 ns/op
BenchmarkLoad/CacheMiss-20                          2925            359599 ns/op
BenchmarkList/Recursive-20                           154           7549510 ns/op
BenchmarkList/NonRecursive-20                        229           5147897 ns/op
BenchmarkList/NonRecursiveSubfolder-20              2778            426876 ns/op
BenchmarkStat-20                                    3358            358434 ns/op
PASS
ok      github.com/root-sector/caddy-storage-mongodb/v2    71.759s
Interpreting Benchmark Results

The output from go test -bench provides several key metrics. For example:

  • ns/op (Nanoseconds per operation): This is the average time taken to execute the benchmarked code once. This is a primary performance indicator. Lower values are better.
  • B/op (Bytes per operation): (Requires -benchmem flag) The average number of bytes allocated on the heap per operation. Lower values are better.
  • allocs/op (Allocations per operation): (Requires -benchmem flag) The average number of distinct memory allocations per operation. Lower values are better.

Lines like --- BENCH: BenchmarkLoad/CacheHit-20 denote the start of logged output (b.Log(...)) for a specific benchmark run. The lines following it, such as storage_bench_test.go:144: Pre-populating..., are the actual log messages from the benchmark code, useful for understanding the setup and execution flow for each benchmark case. This detailed logging appears when go test runs each benchmark function multiple times to stabilize results, or when using the -v flag (especially with -count=1).

Evaluating Performance

When analyzing benchmark results:

  • Compare ns/op: This is the most direct measure of speed. For instance, compare BenchmarkStore/WithBulkWrites vs. BenchmarkStore/WithoutBulkWrites to see the performance impact of bulk writes.
  • Look at B/op and allocs/op: These metrics help understand memory efficiency. Fewer allocations and bytes per operation generally lead to less garbage collector overhead and better overall performance.
  • Cache Impact: Compare BenchmarkLoad/CacheHit vs. BenchmarkLoad/CacheMiss. A significantly lower ns/op for cache hits demonstrates the effectiveness of the Ristretto cache.
  • Consistency: Running benchmarks multiple times (using -count) can help identify variability in performance. Look for consistent results across runs.
  • Context Matters: Absolute numbers are less important than relative differences between configurations or code versions. Use benchmarks to track performance improvements or regressions as the code evolves.

The provided benchmarks (BenchmarkStore, BenchmarkLoad, BenchmarkList) cover fundamental operations. You can expand on these or create new ones to test specific scenarios relevant to your use case.

Debug Logging

The module uses Zap logger for debug logging. Enable debug logging in your Caddy configuration:

{
    debug
    storage mongodb {
        ...
    }
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotExist    = fs.ErrNotExist
	ErrLockExists  = errors.New("lock already exists")
	ErrLockNotHeld = errors.New("lock not held by this instance")
)

Functions

This section is empty.

Types

type MongoDBStorage

type MongoDBStorage struct {
	URI             string        `json:"uri,omitempty"`
	Database        string        `json:"database,omitempty"`
	Collection      string        `json:"collection,omitempty"`
	LocksCollection string        `json:"locks_collection,omitempty"`
	Timeout         time.Duration `json:"timeout,omitempty"`

	CacheTTL        time.Duration `json:"cache_ttl,omitempty"`
	MaxCacheEntries int           `json:"max_cache_entries,omitempty"`
	MaxPoolSize     uint64        `json:"max_pool_size,omitempty"`
	MinPoolSize     uint64        `json:"min_pool_size,omitempty"`
	MaxConnIdleTime time.Duration `json:"max_conn_idle_time,omitempty"`

	EnableBulkWrites  bool          `json:"enable_bulk_writes,omitempty"`
	BulkMaxOps        int           `json:"bulk_max_ops,omitempty"`
	BulkFlushInterval time.Duration `json:"bulk_flush_interval,omitempty"`
	// contains filtered or unexported fields
}

func (*MongoDBStorage) CaddyModule

func (s *MongoDBStorage) CaddyModule() caddy.ModuleInfo

func (*MongoDBStorage) CertMagicStorage

func (s *MongoDBStorage) CertMagicStorage() (certmagic.Storage, error)

func (*MongoDBStorage) Cleanup

func (s *MongoDBStorage) Cleanup() error

func (*MongoDBStorage) Delete

func (s *MongoDBStorage) Delete(ctx context.Context, key string) error

func (*MongoDBStorage) Exists

func (s *MongoDBStorage) Exists(ctx context.Context, key string) bool

func (*MongoDBStorage) Flush

func (s *MongoDBStorage) Flush(ctx context.Context) error

func (*MongoDBStorage) List

func (s *MongoDBStorage) List(ctx context.Context, prefix string, recursive bool) ([]string, error)

func (*MongoDBStorage) Load

func (s *MongoDBStorage) Load(ctx context.Context, key string) ([]byte, error)

func (*MongoDBStorage) Lock

func (s *MongoDBStorage) Lock(ctx context.Context, key string) error

func (*MongoDBStorage) Provision

func (s *MongoDBStorage) Provision(ctx caddy.Context) error

func (*MongoDBStorage) Stat

func (*MongoDBStorage) Store

func (s *MongoDBStorage) Store(ctx context.Context, key string, value []byte) error

func (*MongoDBStorage) Unlock

func (s *MongoDBStorage) Unlock(ctx context.Context, key string) error

func (*MongoDBStorage) UnmarshalCaddyfile

func (s *MongoDBStorage) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

func (*MongoDBStorage) Validate

func (s *MongoDBStorage) Validate() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL