Deploying Galene
Introduction
Galène (or Galene) is a lightweight, open-source videoconferencing server written in Go that uses WebRTC for real-time communication. Unlike heavier solutions that require multiple services and complex infrastructure, Galène runs as a single binary with minimal resource requirements while still supporting features like screen sharing, chat, file sharing, and recording capabilities.
The name comes from the Greek word for “calm seas,” reflecting the project’s philosophy of providing a simple, stable videoconferencing solution. Galène supports multiple simultaneous groups (rooms), different user roles (operator, presenter, observer), and can handle both small team meetings and larger presentations with selective forwarding.
Deploying Galène on Klutch.sh gives you a managed platform with automatic Dockerfile detection, persistent storage for recordings and configuration, secure environment variable management, and easy scaling for your videoconferencing needs. This guide covers everything from initial setup through production deployment, including WebRTC configuration, TURN server integration, and best practices for reliable video meetings.
Why Deploy Galène on Klutch.sh?
Klutch.sh provides several advantages for running Galène in production:
- Automatic Dockerfile Detection: Push your repository and Klutch.sh automatically builds from your Dockerfile
- Persistent Volumes: Store recordings, configuration files, and group definitions reliably across deployments
- Environment Variable Management: Securely store TURN credentials, admin passwords, and other secrets
- HTTP Traffic Support: Serve the web interface and WebSocket signaling over HTTPS
- Custom Domains: Use your own domain for a professional videoconferencing URL
- GitHub Integration: Direct connection to your GitHub repository for continuous deployment
- Scalable Infrastructure: Handle varying meeting loads without managing servers
Prerequisites
Before deploying Galène on Klutch.sh, ensure you have:
- A Klutch.sh account
- A GitHub account with a repository for your project
- Docker installed locally for testing (optional but recommended)
- Basic familiarity with Docker and WebRTC concepts
- A TURN server for reliable NAT traversal (can use public STUN servers for testing)
- Understanding of TLS/HTTPS requirements for WebRTC
What is Galène?
Galène is a videoconferencing server that provides:
- Lightweight Architecture: Single Go binary with minimal dependencies
- WebRTC-Based: Modern peer-to-peer video with selective forwarding unit (SFU) support
- Multiple Rooms: Create and manage multiple concurrent meeting groups
- Role-Based Access: Operators, presenters, and observers with different permissions
- Screen Sharing: Share screens and application windows
- Text Chat: Built-in chat functionality alongside video
- File Sharing: Share files directly within meetings
- Recording: Record meetings locally or server-side
- Simulcast Support: Adaptive bitrate streaming for varying network conditions
- Low Latency: Optimized for real-time communication
Galène Architecture Overview
Understanding Galène’s architecture helps with proper deployment configuration:
┌─────────────────────────────────────────────────────────────────────┐│ Galène Server │├─────────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────────────────────────────────────────┐ ││ │ HTTP/WebSocket Server │ ││ │ (Port 8443/443) │ ││ └──────────────────────────┬──────────────────────────────────┘ ││ │ ││ ┌─────────────────┼─────────────────┐ ││ │ │ │ ││ ┌────────▼────────┐ ┌──────▼──────┐ ┌───────▼───────┐ ││ │ Web Interface │ │ WebSocket │ │ WebRTC │ ││ │ (Static HTML) │ │ Signaling │ │ Media Server │ ││ └─────────────────┘ └─────────────┘ └───────────────┘ ││ │ ││ │ ││ ┌─────────────────────────────────────────────────────────────┐ ││ │ Group Configuration │ ││ │ (JSON files in /groups/) │ ││ └─────────────────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────────┘ │ │ WebRTC Media Streams ▼┌─────────────────────────────────────────────────────────────────────┐│ TURN/STUN Server ││ (NAT Traversal Support) │└─────────────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────────┐│ Client Browsers │├─────────────────────────────────────────────────────────────────────┤│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││ │ Operator │ │ Presenter │ │ Observer │ ... ││ │ (Full │ │ (Can share │ │ (View │ ││ │ control) │ │ media) │ │ only) │ ││ └─────────────┘ └─────────────┘ └─────────────┘ │└─────────────────────────────────────────────────────────────────────┘Key Components:
- HTTP Server: Serves the web interface and handles WebSocket connections
- WebSocket Signaling: Manages session negotiation between clients
- WebRTC Media Server: Handles audio/video streams using SFU (Selective Forwarding Unit)
- Group Configuration: JSON files defining rooms, users, and permissions
- TURN/STUN: External servers for NAT traversal (required for reliable connectivity)
Project Structure
A typical repository layout for deploying Galène on Klutch.sh:
galene-deployment/├── Dockerfile├── docker-entrypoint.sh├── config/│ ├── ice-servers.json│ └── passwd├── groups/│ ├── public.json│ └── private.json├── data/│ └── recordings/├── static/│ └── custom.css (optional)├── .env.example└── README.mdThe groups/ directory contains JSON files that define each meeting room, while config/ holds server-wide settings like TURN server credentials.
Sample Dockerfile
Create a Dockerfile in the root of your repository. Klutch.sh automatically detects and uses it for deployment:
FROM golang:1.21-alpine AS builder
# Install build dependenciesRUN apk add --no-cache git make
# Clone Galène sourceWORKDIR /buildRUN git clone https://github.com/jech/galene.git .
# Build GalèneRUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o galene ./...
# Production imageFROM alpine:3.19
# Install runtime dependenciesRUN apk add --no-cache \ ca-certificates \ tzdata \ curl
# Create galene userRUN adduser -D -u 1000 galene
# Create directoriesRUN mkdir -p /opt/galene /data/groups /data/recordings /data/config && \ chown -R galene:galene /opt/galene /data
# Copy binary from builderCOPY --from=builder /build/galene /opt/galene/COPY --from=builder /build/static /opt/galene/static/
# Copy configuration filesCOPY config/ /data/config/COPY groups/ /data/groups/COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh && \ chown -R galene:galene /data
# Expose HTTP portEXPOSE 8443
WORKDIR /opt/galeneUSER galene
# Health checkHEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ CMD curl -f http://localhost:8443/ || exit 1
ENTRYPOINT ["docker-entrypoint.sh"]CMD ["/opt/galene/galene", "-http", ":8443", "-groups", "/data/groups", "-data", "/data"]Docker Entrypoint Script
Create a docker-entrypoint.sh file to handle initialization and configuration:
#!/bin/shset -e
# Set defaultsGALENE_HTTP_PORT="${GALENE_HTTP_PORT:-8443}"GALENE_TURN_SERVER="${GALENE_TURN_SERVER:-}"GALENE_TURN_USERNAME="${GALENE_TURN_USERNAME:-}"GALENE_TURN_PASSWORD="${GALENE_TURN_PASSWORD:-}"GALENE_ADMIN_PASSWORD="${GALENE_ADMIN_PASSWORD:-}"
echo "Starting Galène Server..."echo "================================"echo "HTTP Port: ${GALENE_HTTP_PORT}"echo "Groups Directory: /data/groups"echo "Data Directory: /data"echo "================================"
# Generate ICE servers configuration if TURN credentials providedif [ -n "$GALENE_TURN_SERVER" ]; then echo "Configuring TURN server..." cat > /data/config/ice-servers.json << EOF[ { "urls": ["stun:stun.l.google.com:19302"] }, { "urls": ["turn:${GALENE_TURN_SERVER}"], "username": "${GALENE_TURN_USERNAME}", "credential": "${GALENE_TURN_PASSWORD}" }]EOF echo "TURN server configured: ${GALENE_TURN_SERVER}"else # Default to public STUN servers only (limited NAT traversal) cat > /data/config/ice-servers.json << EOF[ { "urls": ["stun:stun.l.google.com:19302"] }, { "urls": ["stun:stun1.l.google.com:19302"] }]EOF echo "WARNING: No TURN server configured. NAT traversal may be limited."fi
# Generate admin password file if providedif [ -n "$GALENE_ADMIN_PASSWORD" ]; then # Create password hash (Galène uses SHA-256) HASH=$(echo -n "$GALENE_ADMIN_PASSWORD" | sha256sum | cut -d' ' -f1) echo "admin:${HASH}" > /data/config/passwd echo "Admin credentials configured."fi
# Copy ICE servers config to expected locationcp /data/config/ice-servers.json /data/ice-servers.json 2>/dev/null || true
# Execute the main commandexec "$@"Group Configuration
Create group definition files in the groups/ directory. Each JSON file defines a meeting room.
Public Group (groups/public.json)
{ "displayName": "Public Meeting Room", "description": "Open meeting room for general discussions", "contact": "admin@example.com", "comment": "Anyone can join this room", "max-clients": 50, "allow-recording": true, "allow-anonymous": true, "autolock": false, "public": true, "op": [ { "username": "admin", "password": "admin-password-hash" } ], "presenter": [ {} ], "other": [ {} ]}Private Group (groups/private.json)
{ "displayName": "Private Team Meeting", "description": "Restricted access team room", "contact": "admin@example.com", "max-clients": 20, "allow-recording": true, "allow-anonymous": false, "autolock": true, "public": false, "op": [ { "username": "admin", "password": "sha256-hash-here" } ], "presenter": [ { "username": "presenter1", "password": "sha256-hash-here" } ], "other": [ { "username": "viewer1", "password": "sha256-hash-here" } ]}Generating Password Hashes
Generate SHA-256 password hashes for group configuration:
# Generate hash for a passwordecho -n "your-password" | sha256sum | cut -d' ' -f1
# Or use Pythonpython3 -c "import hashlib; print(hashlib.sha256(b'your-password').hexdigest())"
# Or use OpenSSLecho -n "your-password" | openssl dgst -sha256 | cut -d' ' -f2ICE Servers Configuration
Create config/ice-servers.json for STUN/TURN server configuration:
[ { "urls": ["stun:stun.l.google.com:19302"] }, { "urls": ["stun:stun1.l.google.com:19302"] }, { "urls": ["turn:turn.example.com:3478"], "username": "turnuser", "credential": "turnpassword" }, { "urls": ["turns:turn.example.com:5349"], "username": "turnuser", "credential": "turnpassword" }]Important: For production use, you should deploy your own TURN server or use a managed TURN service. Public STUN servers only help with simple NAT scenarios and won’t work for users behind symmetric NATs or restrictive firewalls.
Environment Variables
Configure these environment variables in the Klutch.sh dashboard:
Required Variables
| Variable | Description | Example |
|---|---|---|
GALENE_HTTP_PORT | Internal HTTP server port | 8443 |
Recommended Variables
| Variable | Description | Example |
|---|---|---|
GALENE_TURN_SERVER | TURN server hostname:port | turn.example.com:3478 |
GALENE_TURN_USERNAME | TURN server username | turnuser |
GALENE_TURN_PASSWORD | TURN server password | secure_password |
GALENE_ADMIN_PASSWORD | Admin user password | admin_password |
Optional Variables
| Variable | Description | Default |
|---|---|---|
TZ | Timezone for logs | UTC |
GALENE_INSECURE | Allow insecure WebSocket | false |
Deploying Galène on Klutch.sh
Follow these steps to deploy Galène on Klutch.sh:
-
Prepare Your Repository
Create a new GitHub repository with the Dockerfile, configuration files, and entrypoint script.
Terminal window # Clone your repositorygit clone https://github.com/yourusername/galene-deployment.gitcd galene-deployment# Create the directory structuremkdir -p config groups data/recordings# Add configuration files# - Dockerfile# - docker-entrypoint.sh# - config/ice-servers.json# - groups/public.json# Commit and pushgit add .git commit -m "Initial Galène deployment configuration"git push origin main -
Log in to Klutch.sh
Navigate to klutch.sh/app and sign in to your account.
-
Create a New Project
Click “New Project” and provide a name for your videoconferencing project (e.g., “galene-meetings”).
-
Create a New App
Within your project, click “New App” and configure:
- App Name:
galene - Git Repository: Connect your GitHub repository
- Branch:
main(or your default branch)
- App Name:
-
Configure Environment Variables
In the app settings, add the following environment variables:
GALENE_HTTP_PORT=8443GALENE_TURN_SERVER=your-turn-server.com:3478GALENE_TURN_USERNAME=your_turn_usernameGALENE_TURN_PASSWORD=your_turn_passwordGALENE_ADMIN_PASSWORD=secure_admin_passwordTZ=UTCMark
GALENE_TURN_PASSWORDandGALENE_ADMIN_PASSWORDas secrets. -
Configure Network Settings
- Traffic Type: HTTP
- Internal Port:
8443
-
Attach Persistent Volume
Galène benefits from persistent storage for recordings and group configurations:
- Click “Add Volume” in the app settings
- Mount Path:
/data - Size: 10GB or more depending on recording needs
-
Deploy the Application
Click “Create” to start the deployment. Klutch.sh will:
- Detect the Dockerfile automatically
- Build the container image
- Deploy with your configured environment variables
- Attach the persistent volume
-
Verify Deployment
Once deployed, access the Galène web interface at:
https://galene.klutch.shYou should see the Galène homepage with a list of available groups.
Setting Up a TURN Server
For reliable WebRTC connectivity, especially for users behind NAT or corporate firewalls, you need a TURN server. You can deploy one on Klutch.sh:
Coturn Dockerfile
FROM coturn/coturn:latest
# Copy custom configurationCOPY turnserver.conf /etc/coturn/turnserver.conf
EXPOSE 3478 3478/udp 5349 5349/udp
CMD ["turnserver", "-c", "/etc/coturn/turnserver.conf"]Coturn Configuration (turnserver.conf)
# Coturn TURN server configurationlistening-port=3478tls-listening-port=5349fingerprintlt-cred-mech
# Use external IP for NAT traversal# external-ip=YOUR_EXTERNAL_IP
# Realm and credentialsrealm=example.comuser=turnuser:turnpassword
# Securityno-tcp-relaydenied-peer-ip=10.0.0.0-10.255.255.255denied-peer-ip=192.168.0.0-192.168.255.255denied-peer-ip=172.16.0.0-172.31.255.255
# Logginglog-file=/var/log/turnserver.logverboseDeploy Coturn as a separate app with TCP traffic on port 3478 mapped to external port 8000.
Using Galène
Joining a Meeting
- Navigate to your Galène URL (e.g., `https://galene.klutch.sh`)
- Select a group from the list or enter a group name directly
- Enter your username and password (if required)
- Allow browser access to camera and microphone
- Click "Join" to enter the meeting
User Roles
Operator: Full control over the meeting
- Can mute/unmute participants
- Can kick users
- Can promote/demote other users
- Can lock/unlock the room
- Can start/stop recording
Presenter: Can share media
- Camera and microphone access
- Screen sharing capability
- File sharing
- Chat participation
Observer: View-only access
- Can view shared media
- Can participate in chat
- Cannot share camera/microphone
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
m | Toggle microphone mute |
v | Toggle camera |
s | Toggle screen share |
h | Toggle hand raise |
c | Toggle chat panel |
f | Toggle fullscreen |
API Integration
Galène provides a WebSocket-based API for programmatic control:
JavaScript Client Example
// Connect to Galène WebSocketconst ws = new WebSocket('wss://galene.klutch.sh/ws');
// Authenticationws.onopen = () => { ws.send(JSON.stringify({ type: 'handshake', id: crypto.randomUUID() }));};
// Handle messagesws.onmessage = (event) => { const message = JSON.parse(event.data);
switch (message.type) { case 'handshake': // Join a group ws.send(JSON.stringify({ type: 'join', kind: 'join', group: 'public', username: 'api-user', password: 'password' })); break;
case 'joined': console.log('Successfully joined group:', message.group); break;
case 'user': console.log('User event:', message); break;
case 'chat': console.log('Chat message:', message.value); break;
case 'error': console.error('Error:', message.value); break; }};
// Send a chat messagefunction sendChat(message) { ws.send(JSON.stringify({ type: 'chat', source: '', dest: '', privileged: false, value: message }));}
// Request to presentfunction requestPresenter() { ws.send(JSON.stringify({ type: 'request', value: 'presenter' }));}Python Client Example
import asyncioimport websocketsimport jsonimport uuid
async def galene_client(): uri = "wss://galene.klutch.sh/ws"
async with websockets.connect(uri) as websocket: # Handshake await websocket.send(json.dumps({ "type": "handshake", "id": str(uuid.uuid4()) }))
# Receive handshake response response = await websocket.recv() data = json.loads(response)
if data["type"] == "handshake": # Join a group await websocket.send(json.dumps({ "type": "join", "kind": "join", "group": "public", "username": "python-bot", "password": "password" }))
# Listen for messages async for message in websocket: data = json.loads(message) print(f"Received: {data['type']}")
if data["type"] == "chat": print(f"Chat: {data['value']}") elif data["type"] == "user": print(f"User event: {data}")
if __name__ == "__main__": asyncio.run(galene_client())Advanced Configuration
Custom Static Files
Override default Galène static files by adding them to the static/ directory:
/* static/custom.css - Custom styling */:root { --main-color: #4a90d9; --background-color: #1a1a2e; --text-color: #ffffff;}
.chat-container { background: rgba(0, 0, 0, 0.7);}
.video-container { border-radius: 8px; overflow: hidden;}Recording Configuration
Enable server-side recording by adding these options to your group configuration:
{ "displayName": "Recording-Enabled Room", "allow-recording": true, "record": "auto", "max-clients": 30}Recording modes:
"auto": Automatically record when an operator joins"manual": Operators must manually start recordingfalse: Disable recording
Simulcast Configuration
Enable adaptive bitrate streaming for better performance on varying networks:
{ "displayName": "Simulcast-Enabled Room", "codecs": ["vp8", "opus"], "max-clients": 100, "max-history-age": 14400}Rate Limiting
Protect your server from abuse:
# Add to Dockerfile CMD or entrypoint/opt/galene/galene \ -http :8443 \ -groups /data/groups \ -data /data \ -max-clients 200 \ -max-clients-per-ip 10Monitoring and Health Checks
Health Check Endpoint
Galène responds to HTTP requests at the root path. Use this for monitoring:
# Check server healthcurl -s -o /dev/null -w "%{http_code}" https://galene.klutch.sh/Metrics and Logging
Configure detailed logging:
# In docker-entrypoint.shexec /opt/galene/galene \ -http :8443 \ -groups /data/groups \ -data /data \ -turn /data/ice-servers.json \ 2>&1 | tee /data/galene.logKey Metrics to Monitor
| Metric | Description | Alert Threshold |
|---|---|---|
| Active connections | Number of connected clients | > 80% of max-clients |
| WebSocket errors | Connection failures | > 5% error rate |
| CPU usage | Server CPU load | > 80% sustained |
| Memory usage | Server memory consumption | > 85% |
| Bandwidth | Network throughput | Based on capacity |
For comprehensive monitoring setup, see the Monitoring guide.
Troubleshooting
Common Issues
Video/Audio Not Working
Error: Failed to access media devicesSolution:
- Ensure HTTPS is used (WebRTC requires secure context)
- Check browser permissions for camera/microphone
- Verify no other application is using the media devices
- Try a different browser or incognito mode
Connection Failures
Error: ICE connection failedSolution:
- Verify TURN server is configured and accessible
- Check TURN credentials are correct
- Ensure firewall allows WebRTC traffic
- Test with public STUN servers first to isolate the issue
Cannot Join Group
Error: Authentication failedSolution:
- Verify username and password are correct
- Check password hash format in group configuration
- Ensure group file has valid JSON syntax
- Review server logs for detailed error messages
Recording Not Working
Error: Recording permission deniedSolution:
- Ensure
allow-recording: truein group configuration - Verify persistent volume is mounted to
/data - Check directory permissions for recordings folder
- Confirm user has operator role
High Latency or Poor Quality
Solution:
- Check network bandwidth and stability
- Verify TURN server performance
- Reduce video resolution in client settings
- Enable simulcast for adaptive quality
- Check server CPU and memory usage
Debug Mode
Enable verbose logging for troubleshooting:
# Add to entrypoint or CMD/opt/galene/galene \ -http :8443 \ -groups /data/groups \ -data /data \ -insecureNote: The -insecure flag is for debugging only; never use in production.
Log Locations
| Component | Log Path |
|---|---|
| Galène Server | /data/galene.log (if configured) |
| Container stdout | View in Klutch.sh logs |
| Recording files | /data/recordings/ |
Security Best Practices
TLS/HTTPS Configuration
- Always use HTTPS in production (Klutch.sh handles this automatically)
- Ensure WebSocket connections use
wss:// - Configure HSTS headers for additional security
Authentication
- Use strong password hashes (SHA-256 minimum)
- Implement different passwords for each group
- Regularly rotate admin credentials
- Consider integrating with external identity providers
Network Security
- Deploy TURN server with authentication
- Use TURNS (TLS-encrypted TURN) when possible
- Limit
max-clients-per-ipto prevent abuse - Monitor for unusual connection patterns
Data Protection
- Enable encryption for recordings at rest
- Implement retention policies for recorded content
- Regular backup of group configurations
- Audit access to sensitive groups
Production Checklist
Before going live with Galène in production:
- Dockerfile tested and builds successfully
- TURN server deployed and accessible
- TURN credentials configured as secrets
- Group configurations created and tested
- Persistent volume attached for recordings/data
- HTTPS configured (automatic on Klutch.sh)
- Admin passwords set and secured
- Max clients limit configured appropriately
- Health check endpoint verified
- Monitoring and alerting configured
- Backup procedures documented
- User documentation prepared
- Browser compatibility tested (Chrome, Firefox, Safari, Edge)
- Mobile browser tested if needed
Additional Resources
- Galène Official Website
- Galène GitHub Repository
- Galène Protocol Documentation
- WebRTC Official Site
- Coturn TURN Server
- Deploying Jitsi (Alternative)
- Deploying MiroTalk (Alternative)
- Klutch.sh Persistent Volumes
- Klutch.sh Monitoring
Conclusion
Galène provides a lightweight yet powerful videoconferencing solution that’s perfect for teams who want full control over their video infrastructure without the complexity of enterprise platforms. Its single-binary architecture makes it easy to deploy and maintain, while still offering essential features like multi-room support, role-based access, and recording capabilities.
By deploying Galène on Klutch.sh, you get the benefits of managed infrastructure—automatic Dockerfile detection, persistent storage, and secure environment management—while maintaining the simplicity that makes Galène appealing. Whether you’re running small team meetings or larger presentations, this deployment gives you a reliable, self-hosted videoconferencing solution that respects your privacy and keeps your data under your control.