Etherpad: Real-Time Collaborative Text Editor
Lightweight real-time document collaboration. Perfect for notes, meetings, and brainstorming.
Introduction
Etherpad is a lightweight, highly customizable, real-time collaborative text editor that runs in your browser. Unlike heavy office suites, Etherpad focuses on one thing and does it exceptionally well: enabling multiple users to edit text documents simultaneously with minimal latency and maximum simplicity.
What is Etherpad?
Etherpad is an open-source collaborative editor that allows multiple users to work on the same document in real-time. Changes appear instantly for all connected users, with each author’s contributions highlighted in their own color. It’s the technology that powered the early days of Google Docs collaboration and has evolved into a mature, plugin-rich platform.
Why Choose Etherpad?
Lightweight and Fast
Etherpad requires minimal resources compared to full office suites. It runs smoothly on a Raspberry Pi with just 512MB RAM, making it perfect for homelabs with limited resources. Pages load in milliseconds, and synchronization happens in real-time with imperceptible latency.
True Real-Time Collaboration
Unlike document-based editors that sync every few seconds, Etherpad transmits every keystroke instantly. Multiple users can type in the same paragraph simultaneously without conflicts. The operational transformation algorithm ensures consistency across all clients.
No Account Required
Want to collaborate quickly? Just share a URL. No sign-ups, no authentication hassles. Users can optionally set their display name, but it’s not required. Perfect for quick team brainstorming sessions or public collaboration.
Extensive Plugin Ecosystem
Over 400 plugins available for features like:
- Markdown support
- Code syntax highlighting
- Video chat integration
- Drawing tools
- Import/export to various formats
- Administration dashboards
- Custom themes
Privacy Focused
Self-hosting Etherpad means your collaborative documents never leave your infrastructure. No third-party services, no data mining, complete control over retention policies.
Etherpad vs Alternatives
| Feature | Etherpad | HedgeDoc | Google Docs | Notion |
|---|---|---|---|---|
| Resource Usage | Very Low | Low | N/A | N/A |
| Real-time Sync | Instant | Instant | Fast | Medium |
| No Login Required | Yes | Optional | No | No |
| Markdown Native | Plugin | Yes | No | Partial |
| Self-Hosted | Yes | Yes | No | No |
| Plugin System | 400+ | Limited | N/A | N/A |
| Video Chat | Plugin | No | Yes | No |
| Best For | Quick Notes | Documentation | Full Office | Knowledge Base |
Architecture Overview
Understanding Etherpad’s architecture:
┌────────────────────────────────────────┐
│ Users (Web Browsers) │
└──────────┬──────────────┬──────────────┘
│ │
┌──────▼──────┐ ┌───▼──────┐
│ WebSocket │ │ HTTP │
└──────┬──────┘ └───┬──────┘
│ │
┌──────▼─────────────▼──────┐
│ Reverse Proxy + SSL │
└──────────┬────────────────┘
│
┌──────────▼────────────┐
│ Etherpad (Node.js) │
│ - Express Server │
│ - Socket.io │
│ - Plugin Manager │
└──────────┬────────────┘
│
┌──────────▼────────────┐
│ Database │
│ PostgreSQL/MySQL │
│ (or dirty.db) │
└───────────────────────┘ Key Components:
- Node.js Server: Core application running on Express
- Socket.io: WebSocket communication for real-time updates
- Database: Stores pads, revisions, and metadata
- Plugin System: Extends functionality
- Reverse Proxy: HTTPS termination and load balancing
Prerequisites
Before deploying Etherpad:
- Docker and Docker Compose installed
- 512MB RAM minimum (1GB recommended)
- 1 CPU core minimum
- 5GB storage space
- Domain name (optional, for SSL)
- Basic understanding of Docker
- Reverse proxy (optional, for HTTPS)
Basic Deployment: Standalone Etherpad
Let’s deploy a simple Etherpad instance using the built-in dirty.db database.
Step 1: Quick Start with Dirty DB
For testing or small deployments (1-10 users):
# Create directory
mkdir -p ~/etherpad && cd ~/etherpad
# Create environment file
cat > .env <<EOF
# Etherpad Configuration
ETHERPAD_TITLE=My Homelab Etherpad
ETHERPAD_PORT=9001
# Admin credentials
ADMIN_PASSWORD=your-secure-admin-password
# Session secret (generate with: openssl rand -hex 32)
SESSION_KEY=$(openssl rand -hex 32)
# Domain
DOMAIN=pad.yourdomain.com
EOF
chmod 600 .env Create docker-compose.yml:
version: '3.8'
services:
etherpad:
image: etherpad/etherpad:latest
container_name: etherpad
restart: unless-stopped
environment:
# Basic configuration
- TITLE=${ETHERPAD_TITLE}
- DEFAULT_PAD_TEXT=Welcome to Etherpad! This is a collaborative document.
# Admin user
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
# Security
- SESSION_KEY=${SESSION_KEY}
- TRUST_PROXY=true
# Performance
- MINIFY=true
ports:
- "${ETHERPAD_PORT}:9001"
volumes:
# Persistent data using dirty.db
- etherpad_data:/opt/etherpad-lite/var
networks:
- etherpad-net
volumes:
etherpad_data:
networks:
etherpad-net:
driver: bridge Launch Etherpad:
# Start the service
docker-compose up -d
# Check logs
docker-compose logs -f etherpad
# Wait for "You can access your Etherpad instance at http://localhost:9001/" Access at http://localhost:9001 - create a new pad and start collaborating!
Production Deployment with PostgreSQL
For production use with better performance and reliability:
Step 1: Complete Stack Configuration
Update .env:
cat > .env <<EOF
# Etherpad Configuration
ETHERPAD_TITLE=Homelab Etherpad
ETHERPAD_PORT=9001
ADMIN_PASSWORD=your-secure-admin-password
SESSION_KEY=$(openssl rand -hex 32)
# PostgreSQL Configuration
POSTGRES_DB=etherpad
POSTGRES_USER=etherpad
POSTGRES_PASSWORD=your-secure-postgres-password
# Domain
DOMAIN=pad.yourdomain.com
EOF Create production docker-compose.yml:
version: '3.8'
services:
# PostgreSQL Database
postgres:
image: postgres:15-alpine
container_name: etherpad-postgres
restart: unless-stopped
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- etherpad-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
# Etherpad Application
etherpad:
image: etherpad/etherpad:latest
container_name: etherpad
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
# Basic Configuration
- TITLE=${ETHERPAD_TITLE}
- DEFAULT_PAD_TEXT=Welcome to ${ETHERPAD_TITLE}!
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
- SESSION_KEY=${SESSION_KEY}
# Database Configuration
- DB_TYPE=postgres
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=${POSTGRES_DB}
- DB_USER=${POSTGRES_USER}
- DB_PASS=${POSTGRES_PASSWORD}
# Performance Settings
- MINIFY=true
- TRUST_PROXY=true
- DISABLE_IP_LOGGING=false
# Security
- REQUIRE_SESSION=false
- EDIT_ONLY=false
- REQUIRE_AUTHENTICATION=false
- REQUIRE_AUTHORIZATION=false
ports:
- "${ETHERPAD_PORT}:9001"
volumes:
# Plugin and settings storage
- etherpad_plugins:/opt/etherpad-lite/node_modules
- etherpad_data:/opt/etherpad-lite/var
# Optional: custom settings
- ./settings.json:/opt/etherpad-lite/settings.json:ro
networks:
- etherpad-net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9001/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
postgres_data:
etherpad_plugins:
etherpad_data:
networks:
etherpad-net:
driver: bridge Launch the stack:
docker-compose up -d
docker-compose logs -f Advanced Configuration with Custom Settings
Create a custom settings.json for advanced control:
{
"title": "My Homelab Etherpad",
"favicon": "favicon.ico",
"skinName": "colibris",
"skinVariants": "dark-toolbar light-background super-light-editor",
"ip": "0.0.0.0",
"port": 9001,
"showSettingsInAdminPage": true,
"dbType": "postgres",
"dbSettings": {
"user": "etherpad",
"host": "postgres",
"database": "etherpad",
"password": "your-password",
"port": 5432
},
"defaultPadText": "Welcome to Etherpad!\n\nThis pad is synchronized as you type.",
"padOptions": {
"noColors": false,
"showControls": true,
"showChat": true,
"showLineNumbers": true,
"useMonospaceFont": false,
"userName": "Anonymous",
"userColor": false,
"rtl": false,
"alwaysShowChat": false,
"chatAndUsers": false,
"lang": "en-us"
},
"padShortcutEnabled": {
"altF9": true,
"altC": true,
"delete": true,
"return": true,
"esc": true,
"cmdShift2": true,
"cmdZ": true,
"cmdY": true,
"cmdB": true,
"cmdI": true,
"cmdU": true,
"cmd5": true,
"cmdShiftL": true,
"cmdShiftN": true,
"cmdShift1": true,
"cmdShiftC": true,
"cmdH": true,
"ctrlHome": true,
"pageUp": true,
"pageDown": true
},
"suppressErrorsInPadText": false,
"requireSession": false,
"editOnly": false,
"minify": true,
"maxAge": 21600,
"abiword": null,
"soffice": null,
"tidyHtml": null,
"allowUnknownFileEnds": true,
"requireAuthentication": false,
"requireAuthorization": false,
"trustProxy": true,
"cookie": {
"sameSite": "Lax",
"sessionLifetime": 864000000,
"sessionRefreshInterval": 86400000
},
"users": {
"admin": {
"password": "your-admin-password",
"is_admin": true
}
},
"socketTransportProtocols": ["websocket", "polling"],
"socketIo": {
"maxHttpBufferSize": 10000000
},
"loadTest": false,
"indentationOnNewLine": false,
"toolbar": {
"left": [
["bold", "italic", "underline", "strikethrough"],
["orderedlist", "unorderedlist", "indent", "outdent"],
["undo", "redo"],
["clearauthorship"]
],
"right": [
["importexport", "timeslider", "savedrevision"],
["settings", "embed"],
["showusers"]
],
"timeslider": [
["timeslider_export", "timeslider_settings", "timeslider_returnToPad"]
]
},
"ep_headings2": {
"enabled": true
},
"loglevel": "INFO",
"logconfig": {
"appenders": [
{
"type": "console"
}
]
}
} Mount in docker-compose.yml:
volumes:
- ./settings.json:/opt/etherpad-lite/settings.json:ro Plugin Installation and Management
Etherpad’s power comes from its plugin ecosystem.
Essential Plugins
Install via Admin panel or environment variables:
environment:
# Install plugins on startup
- INSTALL_PLUGINS=ep_headings2,ep_markdown,ep_comments_page,ep_align,ep_font_color,ep_table_of_contents,ep_author_hover,ep_adminpads2 Recommended Plugins:
- ep_headings2: Add heading styles (H1-H6)
- ep_markdown: Markdown support
- ep_comments_page: Add comments to pads
- ep_align: Text alignment options
- ep_font_color: Text and background colors
- ep_table_of_contents: Auto-generate TOC
- ep_author_hover: Show author on hover
- ep_adminpads2: Enhanced admin dashboard
- ep_draw: Embedded drawing tool
- ep_embedded_hyperlinks2: Rich link previews
- ep_copy_paste_images: Paste images directly
- ep_page_view: Page view mode
- ep_print: Enhanced printing
Manual Plugin Installation
# Enter container
docker exec -it etherpad bash
# Install plugin
cd /opt/etherpad-lite
npm install ep_markdown
# Restart Etherpad
exit
docker-compose restart etherpad Via Admin Panel
- Access
http://pad.yourdomain.com/admin - Login with admin credentials
- Go to Plugin Manager
- Search and install plugins
- Restart Etherpad
Reverse Proxy Configuration
Nginx Configuration
# /etc/nginx/sites-available/etherpad.conf
upstream etherpad {
server localhost:9001;
}
server {
listen 80;
server_name pad.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name pad.yourdomain.com;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/pad.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pad.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# Increase timeouts for long-polling
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
location / {
proxy_pass http://etherpad;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Static resources
location ~ ^/(static|javascripts|stylesheets|images)/ {
proxy_pass http://etherpad;
proxy_cache_valid 200 30d;
add_header Cache-Control "public, immutable";
}
} Traefik Configuration
services:
etherpad:
labels:
- "traefik.enable=true"
- "traefik.http.routers.etherpad.rule=Host(`pad.yourdomain.com`)"
- "traefik.http.routers.etherpad.entrypoints=websecure"
- "traefik.http.routers.etherpad.tls.certresolver=letsencrypt"
- "traefik.http.services.etherpad.loadbalancer.server.port=9001"
# WebSocket support
- "traefik.http.middlewares.etherpad-headers.headers.customrequestheaders.X-Forwarded-Proto=https" Caddy Configuration
pad.yourdomain.com {
reverse_proxy etherpad:9001 {
header_up X-Forwarded-Proto {scheme}
}
} Authentication and Security
Enable User Authentication
Update settings.json:
{
"requireAuthentication": true,
"requireAuthorization": false,
"users": {
"admin": {
"password": "admin-password",
"is_admin": true
},
"user1": {
"password": "user1-password",
"is_admin": false
}
}
} LDAP Integration
Install ep_ldapauth plugin:
environment:
- INSTALL_PLUGINS=ep_ldapauth Configure in settings.json:
{
"ep_ldapauth": {
"url": "ldap://ldap.yourdomain.com",
"accountBase": "ou=users,dc=yourdomain,dc=com",
"accountPattern": "(&(objectClass=person)(uid={{username}}))",
"displayNameAttribute": "cn",
"searchDN": "cn=admin,dc=yourdomain,dc=com",
"searchPWD": "admin-password",
"groupSearchBase": "ou=groups,dc=yourdomain,dc=com",
"groupAttribute": "member",
"groupAttributeIsDN": true
}
} Rate Limiting
Protect against abuse:
{
"commitRateLimiting": {
"duration": 1,
"points": 10
}
} Performance Optimization
Database Tuning
For PostgreSQL, optimize connection pooling:
{
"dbSettings": {
"max": 20,
"min": 2,
"idle": 10000,
"acquire": 30000,
"evict": 1000
}
} Enable Caching
{
"minify": true,
"maxAge": 86400,
"cache": {
"type": "memory",
"maxAge": 86400000
}
} Resource Limits
services:
etherpad:
deploy:
resources:
limits:
memory: 2G
cpus: '2'
reservations:
memory: 512M
cpus: '0.5' Monitoring and Maintenance
Admin Dashboard
Access at https://pad.yourdomain.com/admin
Features:
- Active pads list
- User statistics
- Plugin management
- Pad management
- Settings editor
Health Monitoring
# Check health
curl https://pad.yourdomain.com/stats
# Monitor logs
docker-compose logs -f etherpad
# Resource usage
docker stats etherpad Backup Strategy
# Create backup script
cat > ~/etherpad-backup.sh <<'EOF'
#!/bin/bash
BACKUP_DIR="/mnt/backups/etherpad"
DATE=$(date +%Y%m%d_%H%M%S)
# Backup database
docker exec etherpad-postgres pg_dump -U etherpad etherpad > $BACKUP_DIR/etherpad-db-$DATE.sql
# Backup settings
docker cp etherpad:/opt/etherpad-lite/settings.json $BACKUP_DIR/settings-$DATE.json
# Backup plugins
docker exec etherpad tar czf /tmp/plugins-$DATE.tar.gz -C /opt/etherpad-lite/node_modules .
docker cp etherpad:/tmp/plugins-$DATE.tar.gz $BACKUP_DIR/
# Remove old backups
find $BACKUP_DIR -name "*.sql" -mtime +30 -delete
find $BACKUP_DIR -name "*.json" -mtime +30 -delete
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete
echo "Backup completed: $DATE"
EOF
chmod +x ~/etherpad-backup.sh
# Schedule daily backups
crontab -e
# Add: 0 3 * * * /home/user/etherpad-backup.sh Integration with Nextcloud
While Etherpad doesn’t have native Nextcloud integration like ONLYOFFICE or Collabora, you can embed it:
# Install Etherpad Nextcloud app
docker exec -u www-data nextcloud php occ app:install etherpad_lite
# Configure Etherpad URL
docker exec -u www-data nextcloud php occ config:app:set etherpad_lite etherpad_url --value="https://pad.yourdomain.com"
# Set API key (if using ep_api_key plugin)
docker exec -u www-data nextcloud php occ config:app:set etherpad_lite etherpad_api_key --value="your-api-key" Troubleshooting
Issue: Pads Not Saving
# Check database connection
docker exec etherpad npm run check-db
# Verify database
docker exec etherpad-postgres psql -U etherpad -d etherpad -c "dt"
# Check logs
docker-compose logs etherpad | grep -i error Issue: WebSocket Connection Failed
# Verify WebSocket in browser console
# Should see: ws://pad.yourdomain.com/socket.io/?...
# Check reverse proxy WebSocket support
# Nginx: ensure "proxy_set_header Upgrade" is set
# Traefik: check middleware configuration
# Test direct connection
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" http://localhost:9001/socket.io/ Issue: Slow Performance
# Enable minification
# In settings.json: "minify": true
# Increase database pool
# In settings.json dbSettings: "max": 20
# Check resource usage
docker stats etherpad
# Upgrade to PostgreSQL if using dirty.db Issue: Plugin Installation Failed
# Enter container and install manually
docker exec -it etherpad bash
cd /opt/etherpad-lite
npm install ep_markdown --no-save
# Check plugin compatibility
npm outdated
# Restart Etherpad
docker-compose restart etherpad Advanced Use Cases
Public Collaboration Platform
{
"requireAuthentication": false,
"editOnly": false,
"defaultPadText": "Share this URL with collaborators!",
"padOptions": {
"showChat": true,
"showLineNumbers": true
}
} Private Team Workspace
{
"requireAuthentication": true,
"requireAuthorization": true,
"editOnly": true,
"users": {
"team-member-1": {"password": "pass1", "is_admin": false},
"team-member-2": {"password": "pass2", "is_admin": false}
}
} Education Platform
Install plugins for education:
- ep_copy_paste_images
- ep_draw
- ep_comments_page
- ep_page_view
- ep_print
Conclusion
You now have a fully functional Etherpad deployment providing:
- Lightweight, real-time collaborative editing
- Extensive plugin ecosystem
- Flexible authentication options
- Production-ready performance
- Complete data privacy
Next Steps
- Install essential plugins for your use case
- Configure authentication if needed
- Set up automated backups
- Integrate with other services
- Customize themes and branding
Resources
Your real-time collaborative platform is ready for production use!