Production deployment of better-ccflare as a systemd service on Linux.
[Unit]
Description=better-ccflare proxy
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=better-ccflare
Group=better-ccflare
# --- Environment ---
Environment=PORT=8889
Environment=BUN_JSC_forceRAMSize=2147483648
# --- Preflight: strip invalid BUN_JSC_* vars before Bun starts ---
ExecStartPre=/opt/better-ccflare/scripts/preflight-env.sh
# --- Main process ---
# --smol enables aggressive GC (the correct way to reduce memory usage)
ExecStart=/usr/bin/better-ccflare --smol --serve --port 8889
# --- Resource limits ---
MemoryMax=3G
MemoryHigh=2G
CPUQuota=200%
# --- Restart policy ---
Restart=always
RestartSec=5
StartLimitIntervalSec=120
StartLimitBurst=5
# --- Security hardening ---
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/better-ccflare
PrivateTmp=true
# --- Logging ---
StandardOutput=journal
StandardError=journal
SyslogIdentifier=better-ccflare
[Install]
WantedBy=multi-user.targetThe --smol CLI flag is Bun's supported mechanism for reducing memory usage. It enables aggressive garbage collection and is safe for production use:
ExecStart=/usr/bin/better-ccflare --smol --serve --port 8889This is equivalent to JavaScriptCore's "small heap" mode but exposed through Bun's stable CLI interface.
Bun exposes BUN_JSC_* environment variables that map to internal JavaScriptCore options. These are unstable, undocumented, and change between Bun versions.
The critical problem: Bun validates these variables in C++ runtime before any JavaScript executes. If an invalid variable is set, Bun exits with code 1 immediately. No user code can catch or prevent this crash.
Known-valid variables (use with caution, may break in future Bun versions):
| Variable | Purpose | Example |
|---|---|---|
BUN_JSC_forceRAMSize |
Cap JSC heap size in bytes | 2147483648 (2 GB) |
BUN_JSC_useJIT |
Disable JIT compilation | 0 |
BUN_JSC_forceGCSlowPaths |
Force slow GC paths (debug) | 1 |
Invalid variables that will crash Bun:
| Variable | Why it fails |
|---|---|
BUN_JSC_smallHeap |
Not a real JSC option despite the name |
BUN_JSC_aggressiveGC |
Not a real JSC option |
| Any typo or guess | JSC option validation is strict |
Use --smol for memory tuning. Use BUN_JSC_forceRAMSize only if you need a specific heap cap. Avoid all other BUN_JSC_* variables unless you have verified them against Bun's source code for your exact version.
The scripts/preflight-env.sh script strips invalid BUN_JSC_* environment variables before Bun starts. It uses an allowlist of known-valid variables and unsets anything else with a warning to stderr.
ExecStartPre=/opt/better-ccflare/scripts/preflight-env.sh
ExecStart=/usr/bin/better-ccflare --smol --serve --port 8889systemd runs ExecStartPre before the main process. If a stale or invalid BUN_JSC_* variable exists in the environment (from a previous configuration, an inherited environment, or a mistake in the unit file), the preflight script catches it.
When running better-ccflare outside systemd, source the script before starting:
. /opt/better-ccflare/scripts/preflight-env.sh
exec bun run better-ccflare --smol --serveMemoryMax=3G # Hard kill if exceeded (OOM)
MemoryHigh=2G # Kernel applies memory pressure, reclaims pagesSet MemoryMax above what the process actually needs. MemoryHigh applies back-pressure before the hard limit. Combined with --smol, this keeps better-ccflare within predictable bounds.
CPUQuota=200% # Allow up to 2 CPU coresAdjust based on your expected request volume. For most single-proxy deployments, 100-200% is sufficient.
Restart=always
RestartSec=5
StartLimitIntervalSec=120
StartLimitBurst=5This means:
- systemd restarts the process on any exit (including crashes)
- It waits 5 seconds between restart attempts
- If the process crashes 5 times within 120 seconds, systemd stops trying and marks the unit as failed
- After a
StartLimitBurstfailure, manual intervention is required:systemctl reset-failed better-ccflare && systemctl start better-ccflare
Without the preflight script, an invalid BUN_JSC_* variable would burn through all 5 restart attempts in ~25 seconds, causing total proxy downtime until an operator notices.
Symptom: Service fails immediately on start, burns through StartLimitBurst, then stays in failed state.
Cause: BUN_JSC_smallHeap=1 (or similar invalid variable) set in the unit file or inherited environment. Bun validates this in C++ before JavaScript runs, exits code 1.
Fix: Remove the invalid variable from the unit file. Use --smol flag on ExecStart instead. Add the preflight script as ExecStartPre to prevent recurrence.
# Check journal for the crash
journalctl -u better-ccflare --no-pager -n 20
# Reset the failed state
systemctl reset-failed better-ccflare
# Fix the unit file, then reload and start
systemctl daemon-reload
systemctl start better-ccflareAfter editing a unit file, you must run systemctl daemon-reload before restarting the service. Otherwise systemd uses the cached version.
The reference unit file uses a dedicated better-ccflare user. Create it:
useradd --system --no-create-home --shell /usr/sbin/nologin better-ccflare
mkdir -p /var/lib/better-ccflare
chown better-ccflare:better-ccflare /var/lib/better-ccflareIf using the default SQLite database, ensure the service user has write access:
mkdir -p /var/lib/better-ccflare
chown better-ccflare:better-ccflare /var/lib/better-ccflareSet the database path in the unit file:
Environment=BETTER_CCFLARE_DB_PATH=/var/lib/better-ccflare/better-ccflare.db