Skip to content

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.md

The 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 dependencies
RUN apk add --no-cache git make
# Clone Galène source
WORKDIR /build
RUN git clone https://github.com/jech/galene.git .
# Build Galène
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o galene ./...
# Production image
FROM alpine:3.19
# Install runtime dependencies
RUN apk add --no-cache \
ca-certificates \
tzdata \
curl
# Create galene user
RUN adduser -D -u 1000 galene
# Create directories
RUN mkdir -p /opt/galene /data/groups /data/recordings /data/config && \
chown -R galene:galene /opt/galene /data
# Copy binary from builder
COPY --from=builder /build/galene /opt/galene/
COPY --from=builder /build/static /opt/galene/static/
# Copy configuration files
COPY 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 port
EXPOSE 8443
WORKDIR /opt/galene
USER galene
# Health check
HEALTHCHECK --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/sh
set -e
# Set defaults
GALENE_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 provided
if [ -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 provided
if [ -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 location
cp /data/config/ice-servers.json /data/ice-servers.json 2>/dev/null || true
# Execute the main command
exec "$@"

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:

Terminal window
# Generate hash for a password
echo -n "your-password" | sha256sum | cut -d' ' -f1
# Or use Python
python3 -c "import hashlib; print(hashlib.sha256(b'your-password').hexdigest())"
# Or use OpenSSL
echo -n "your-password" | openssl dgst -sha256 | cut -d' ' -f2

ICE 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

VariableDescriptionExample
GALENE_HTTP_PORTInternal HTTP server port8443
VariableDescriptionExample
GALENE_TURN_SERVERTURN server hostname:portturn.example.com:3478
GALENE_TURN_USERNAMETURN server usernameturnuser
GALENE_TURN_PASSWORDTURN server passwordsecure_password
GALENE_ADMIN_PASSWORDAdmin user passwordadmin_password

Optional Variables

VariableDescriptionDefault
TZTimezone for logsUTC
GALENE_INSECUREAllow insecure WebSocketfalse

Deploying Galène on Klutch.sh

Follow these steps to deploy Galène on Klutch.sh:

  1. Prepare Your Repository

    Create a new GitHub repository with the Dockerfile, configuration files, and entrypoint script.

    Terminal window
    # Clone your repository
    git clone https://github.com/yourusername/galene-deployment.git
    cd galene-deployment
    # Create the directory structure
    mkdir -p config groups data/recordings
    # Add configuration files
    # - Dockerfile
    # - docker-entrypoint.sh
    # - config/ice-servers.json
    # - groups/public.json
    # Commit and push
    git add .
    git commit -m "Initial Galène deployment configuration"
    git push origin main
  2. Log in to Klutch.sh

    Navigate to klutch.sh/app and sign in to your account.

  3. Create a New Project

    Click “New Project” and provide a name for your videoconferencing project (e.g., “galene-meetings”).

  4. 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)
  5. Configure Environment Variables

    In the app settings, add the following environment variables:

    GALENE_HTTP_PORT=8443
    GALENE_TURN_SERVER=your-turn-server.com:3478
    GALENE_TURN_USERNAME=your_turn_username
    GALENE_TURN_PASSWORD=your_turn_password
    GALENE_ADMIN_PASSWORD=secure_admin_password
    TZ=UTC

    Mark GALENE_TURN_PASSWORD and GALENE_ADMIN_PASSWORD as secrets.

  6. Configure Network Settings

    • Traffic Type: HTTP
    • Internal Port: 8443
  7. 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
  8. 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
  9. Verify Deployment

    Once deployed, access the Galène web interface at:

    https://galene.klutch.sh

    You 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 configuration
COPY 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 configuration
listening-port=3478
tls-listening-port=5349
fingerprint
lt-cred-mech
# Use external IP for NAT traversal
# external-ip=YOUR_EXTERNAL_IP
# Realm and credentials
realm=example.com
user=turnuser:turnpassword
# Security
no-tcp-relay
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
# Logging
log-file=/var/log/turnserver.log
verbose

Deploy Coturn as a separate app with TCP traffic on port 3478 mapped to external port 8000.


Using Galène

Joining a Meeting

  1. Navigate to your Galène URL (e.g., `https://galene.klutch.sh`)
  2. Select a group from the list or enter a group name directly
  3. Enter your username and password (if required)
  4. Allow browser access to camera and microphone
  5. 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

ShortcutAction
mToggle microphone mute
vToggle camera
sToggle screen share
hToggle hand raise
cToggle chat panel
fToggle fullscreen

API Integration

Galène provides a WebSocket-based API for programmatic control:

JavaScript Client Example

// Connect to Galène WebSocket
const ws = new WebSocket('wss://galene.klutch.sh/ws');
// Authentication
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'handshake',
id: crypto.randomUUID()
}));
};
// Handle messages
ws.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 message
function sendChat(message) {
ws.send(JSON.stringify({
type: 'chat',
source: '',
dest: '',
privileged: false,
value: message
}));
}
// Request to present
function requestPresenter() {
ws.send(JSON.stringify({
type: 'request',
value: 'presenter'
}));
}

Python Client Example

import asyncio
import websockets
import json
import 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 recording
  • false: 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:

Terminal window
# Add to Dockerfile CMD or entrypoint
/opt/galene/galene \
-http :8443 \
-groups /data/groups \
-data /data \
-max-clients 200 \
-max-clients-per-ip 10

Monitoring and Health Checks

Health Check Endpoint

Galène responds to HTTP requests at the root path. Use this for monitoring:

Terminal window
# Check server health
curl -s -o /dev/null -w "%{http_code}" https://galene.klutch.sh/

Metrics and Logging

Configure detailed logging:

Terminal window
# In docker-entrypoint.sh
exec /opt/galene/galene \
-http :8443 \
-groups /data/groups \
-data /data \
-turn /data/ice-servers.json \
2>&1 | tee /data/galene.log

Key Metrics to Monitor

MetricDescriptionAlert Threshold
Active connectionsNumber of connected clients> 80% of max-clients
WebSocket errorsConnection failures> 5% error rate
CPU usageServer CPU load> 80% sustained
Memory usageServer memory consumption> 85%
BandwidthNetwork throughputBased on capacity

For comprehensive monitoring setup, see the Monitoring guide.


Troubleshooting

Common Issues

Video/Audio Not Working

Error: Failed to access media devices

Solution:

  • 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 failed

Solution:

  • 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 failed

Solution:

  • 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 denied

Solution:

  • Ensure allow-recording: true in 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:

Terminal window
# Add to entrypoint or CMD
/opt/galene/galene \
-http :8443 \
-groups /data/groups \
-data /data \
-insecure

Note: The -insecure flag is for debugging only; never use in production.

Log Locations

ComponentLog Path
Galène Server/data/galene.log (if configured)
Container stdoutView 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-ip to 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


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.