Bokeh is a powerful Python library for interactive data visualization, but apps at scale can suffer from memory leaks and excessive RAM usage—especially when used with Holoviews, Panel, Datashader, large dataframes, or custom session logic. This article provides actionable techniques for professional developers to minimize Bokeh server memory consumption, grounded in real-world debugging and best-practice discussions from core contributors and large app deployments.
Understanding Bokeh Memory Management
- Sessions and Documents: Every browser tab creates a session and Python
Document
. On tab close, Bokeh strives to clean up but may fail if references remain—leading to memory leaks. - Garbage Collection: Python’s garbage collector releases memory only if there are no lingering references (cycles or external objects can prevent cleanup).
- Common leak sources: Orphaned references in callbacks, widgets/hooks, external libraries (Panel/Holoviews), or custom data caches.
Best Practices for Reducing Memory Usage
-
Profile & Monitor Memory
- Use
tracemalloc
ormemory_profiler
(mprof
) to identify which objects are consuming RAM. - Monitor Bokeh logs for “extra unexpected referrers” errors—these often signal stuck references.
- Use
-
Session Lifetime & Cleanup Configuration
- Start
bokeh serve
with--unused-session-lifetime
and--check-unused-sessions
to periodically remove idle sessions:panel serve app.py --check-unused-sessions 5000 --unused-session-lifetime 10000
Values are in milliseconds; try shorter lifetimes during development, but validate production behavior for real-world users. - Implement custom cleanup logic using server lifecycle hooks (supported in Bokeh 1.0+):
# server_lifecycle.py def on_session_destroyed(session_context): # Release resources: close files, DB, clear caches pass
curdoc().on_session_destroyed(on_session_destroyed)
can be used directly on a Bokeh Document for per-app logic.
- Start
-
Minimize Persistent References
- Avoid, or carefully manage, global/module-level objects retaining references to plots, widgets, or session data. Use
session_context
for per-session data that Bokeh can automatically free. - Remove references in callbacks, hooks, or signal handling that may be outside the session’s scope. Clean up explicit caches on session end.
- Avoid, or carefully manage, global/module-level objects retaining references to plots, widgets, or session data. Use
-
Holoviews, Panel, and Datashader Integration
- Memory leaks are often due to custom “opts(hooks=…)”, inaccessible widget references, or persistent objects in
@pn.depends
decorated callbacks. If leaks exist, comment out suspect code and test memory over dozens refreshes. - Test smaller, minimized scripts to isolate which code is causing leaks.
- Memory leaks are often due to custom “opts(hooks=…)”, inaccessible widget references, or persistent objects in
-
Release Large Dataframes and Images
- Explicitly delete dataframes or image arrays after use:
del df
and considergc.collect()
(importgc
). - Store only necessary subset data per session, load lazily or persist outside session scope where feasible.
- Explicitly delete dataframes or image arrays after use:
-
Monitor Memory at Scale
- Deploy with memory limits on containers (Docker, Kubernetes).
- Consider autoscaling or process recycling if RAM grows unbounded—especially for multi-user public apps.
Advanced: Custom Document Lifecycle Hooks
From Bokeh GitHub #8247:
“When a bokeh server session is destroyed theDocument
goes through great lengths to clean up any modules that were created. Additionally, it’s possible to define server lifecycle hooks—ideally directly on the Document—for custom cleanup (e.g. closing DB connections, releasing file handles, clearing session-specific caches). Bokeh now supportsDocument.on_session_destroyed
for such custom logic.”
from bokeh.io import curdoc def cleanup(session_context): # Custom cleanup logic here pass curdoc().on_session_destroyed(cleanup)
Troubleshooting: When Memory is Not Released
- If memory is not released after session close, check for circular references, custom hooks, or lingering widgets in callbacks.
- If using Holoviews/Panel, memory leaks may propagate from them, not Bokeh directly.
- Test using totally minimal example scripts and gradually add features, watching for unbounded memory growth.
- Use Bokeh log level
debug
to get insight on session/cleanup activity:bokeh serve app.py --log-level debug
- Watch for errors like “extra unexpected referrers!” indicating leaked references. Investigate any references held outside session scope.
Quick Reference: Key Settings and CLI Options
- –unused-session-lifetime: Maximum lifetime for unused sessions (milliseconds)
- –check-unused-sessions: How often to check for unused sessions (milliseconds)
- on_session_destroyed: Register cleanup functions for session end
- BOKEH_SIGN_SESSIONS, BOKEH_SIMPLE_IDS: Environment variables for session security and object reference control (docs)
Summary Checklist
- Profile memory usage early and often as you build.
- Use per-session lifetimes and cleanup hooks, never rely on global state for session-specific data.
- If using custom Panels, widgets, or hooks, always audit for lingering object references or closures.
- Deploy with memory limits and autoscaling when RAM is critical.
- Stay up to date with Bokeh releases—many memory issues have been incrementally fixed recently, but some will require redesign.