Apache Zeppelin Tutorial: Interactive Notebooks for Data Science (2026)
Apache Zeppelin provides a web-based notebook environment for interactive data analysis with multi-language support. After deploying Zeppelin for collaborative data science teams, I appreciate how it bridges the gap between exploratory analysis and production pipelines by sharing the same Spark/Hive backends used in production.
This tutorial covers Zeppelin's interpreter architecture, notebook management, Spark/Hive integration, visualization capabilities, and deployment strategies for team-based data science workflows.
Zeppelin Architecture and Interpreters
Zeppelin uses interpreters to execute code in different languages. Each interpreter runs in its own JVM process, isolating resources and failures. The Spark interpreter manages SparkContext lifecycle and provides magics for SQL, Python, and Scala. The JDBC interpreter connects to databases. The Shell interpreter runs system commands.
Interpreters are configurable per-notebook and can be shared across users. Zeppelin manages interpreter processes, restarting them on failure and reclaiming resources after idle timeouts.
%spark
// Scala Spark interpreter
val data = spark.read.parquet("/data/events.parquet")
data.filter($"event_type" === "purchase")
.groupBy($"region")
.agg(sum($"amount").alias("revenue"), count("*").alias("orders"))
.orderBy(col("revenue").desc)
.show(10)
%spark.pyspark
# Python Spark interpreter
df = spark.read.parquet("/data/events.parquet")
df.filter(df.event_type == "purchase") \
.groupBy("region") \
.agg({"amount": "sum", "*": "count"}) \
.show()
%sql
-- SparkSQL interpreter
SELECT region, sum(amount) AS revenue
FROM events
WHERE event_type = 'purchase'
GROUP BY region
ORDER BY revenue DESC
Notebook Development and Collaboration
Notebooks contain paragraphs of code, markdown, and visualizations. Each paragraph runs independently and can depend on variables defined in previous paragraphs. Zeppelin tracks notebook history with version control and allows reverting to previous states.
Notebooks are stored as JSON files on the filesystem (default: notebooks/). For team use, configure Zeppelin with Shiro authentication and LDAP integration to manage access control. Notebooks can be published and shared via URLs.
# Zeppelin configuration
# zeppelin-site.xml
zeppelin.anonymous.allowed
false
zeppelin.notebook.dir
/opt/zeppelin/notebook
# LDAP authentication
zeppelin.shiro.realm
LDAPRealm
# Create a notebook via REST API
curl -X POST http://localhost:8080/api/notebook \
-H 'Content-Type: application/json' \
-d '{"name": "Data Analysis", "paragraphs": []}'
SQL and JDBC Integration
The JDBC interpreter connects Zeppelin to any JDBC-compatible database: PostgreSQL, MySQL, Hive, Presto, Trino, and others. Configure connection parameters, and write SQL directly in notebook paragraphs. Results display as interactive tables with sorting, filtering, and export.
SparkSQL paragraphs use the Spark interpreter and can query Spark SQL tables, Hive Metastore tables, and Delta Lake tables. This lets analysts write SQL against the same data sources used in production ETL.
%jdbc
-- PostgreSQL connection
-- Configuration: jdbc.postgresql.url=jdbc:postgresql://db:5432/analytics
-- Configuration: jdbc.postgresql.user=analyst
SELECT date_trunc('day', created_at) AS day,
count(*) AS signups
FROM users
WHERE created_at >= '2026-01-01'
GROUP BY 1
ORDER BY 1;
%jdbc
-- Hive via JDBC
-- Configuration: jdbc.hive.url=jdbc:hive2://hiveserver:10000/default
SELECT * FROM transactions
WHERE partition_date = '2026-01-01'
LIMIT 100;
%spark.sql
-- Direct SparkSQL
SHOW TABLES IN default;
DESCRIBE FORMATTED events;
SELECT * FROM events WHERE dt = current_date();
Visualization and Dynamic Forms
Zeppelin automatically visualizes query results as tables, bar charts, line charts, pie charts, scatter plots, and heatmaps. Click the chart icon to switch visualization types. Dynamic forms let you create parameterized notebooks with dropdowns, text inputs, and sliders that feed into queries.
For custom visualizations, use AngularJS paragraphs or JavaScript-based extensions. Zeppelin integrates with D3.js and other visualization libraries for custom rendering.
%spark
// Dynamic forms with variables
val region = z.select("Region", Seq("us-east", "us-west", "eu-west", "ap-south"))
val start_date = z.textbox("Start Date", defaultValue = "2026-01-01")
val end_date = z.textbox("End Date", defaultValue = "2026-12-31")
spark.sql(s"""
SELECT event_type, count(*) AS cnt
FROM events
WHERE region = '$region'
AND event_date BETWEEN '$start_date' AND '$end_date'
GROUP BY event_type
ORDER BY cnt DESC
""").show()
// Custom D3 visualization
%angular
Zeppelin and Production Workflows
Zeppelin notebooks can be scheduled via the REST API or integrated with Airflow for production workflows. Export notebook paragraphs as scripts for production execution. Zeppelin's interpreter isolation means notebook exploration uses the same Spark/Hive infrastructure as production jobs.
For reproducible analysis, commit notebooks to Git. Use Zeppelin's notebook import/export for version control. Export as JSON, transform with scripts, and reimport for CI/CD integration.
# Export notebook via REST API
curl -o notebook.json http://localhost:8080/api/notebook/export?id=NOTEBOOK_ID
# Convert notebook to Python script
import json
with open('notebook.json') as f:
nb = json.load(f)
for para in nb['paragraphs']:
if para.get('text', '').startswith('%spark.pyspark'):
print(para['text'].replace('%spark.pyspark', ''))
# Schedule with Airflow
from airflow import DAG
from airflow.providers.apache.zeppelin.operators.zeppelin import ZeppelinOperator
with DAG('notebook_daily', schedule='@daily') as dag:
notebook = ZeppelinOperator(
task_id='run_analysis',
zeppelin_conn_id='zeppelin_default',
note_id='analysis_notebook_id'
)
Deployment and Security Configuration
Zeppelin runs as a web application on port 8080 by default. For production, deploy behind a reverse proxy with TLS. Configure interpreter settings in zeppelin-site.xml and interpreter.json. For multi-tenant deployments, enable Shiro authentication and LDAP integration.
Resource management is critical: each interpreter consumes memory and CPU. Configure interpreter limits to prevent one user from monopolizing resources. For Spark interpreters, set spark.driver.memory and spark.executor.memory appropriately.
# zeppelin-env.sh
export ZEPPELIN_MEM="-Xmx4g"
export ZEPPELIN_INTP_MEM="-Xmx2g"
export ZEPPELIN_SPARK_MAXRALLEL="16"
export ZEPPELIN_INTERPRETERS="spark,jdbc,markdown,shell"
# interpreter.json — Spark interpreter config
{
"spark": {
"option.hive": {
"name": "Hive",
"properties": {
"spark.master": "yarn",
"spark.submit.deployMode": "client",
"spark.yarn.maxAppAttempts": 1
}
},
"option.default": {
"name": "default",
"properties": {
"spark.driver.memory": "4g",
"spark.executor.memory": "8g",
"spark.executor.cores": 4
}
}
}
}
# Start Zeppelin
$ bin/zeppelin-daemon.sh start
Frequently Asked Questions
How does Zeppelin differ from Jupyter?
Zeppelin has built-in Spark integration with SparkContext management and multi-user support. Jupyter requires kernel processes per user. Zeppelin's interpreter model is more resource-efficient for shared Spark clusters.
Can Zeppelin run multiple Spark versions?
Yes, each interpreter can use a different Spark version. Configure separate interpreter instances for different Spark versions and assign them to different notebooks.
How do I secure Zeppelin in production?
Enable Shiro authentication with LDAP, configure TLS via reverse proxy, set interpreter resource limits, and use notebook-level permissions to control access.
Can Zeppelin schedule notebook execution?
Zeppelin provides a cron-like scheduler in the UI. For production, use the REST API with Airflow or another orchestrator for better monitoring and retry capabilities.
Originally published on Ayodhyyya. Last updated June 1, 2026.