A minimalistic, self-hostable WebRTC voice chat.
Client: React 19, TypeScript, Vite, Zustand, Socket.IO Client
Server: Express, Socket.IO Server, Bun
Infra: Docker, nginx, coturn, Let's Encrypt
- Server: Linux VPS with 1 GB RAM or more
- Domain: Any domain pointing to your server's IP (free options: FreeDNS, DuckDNS)
- Docker
- Open Ports: 80 (HTTP), 443 (HTTPS), 3478 (TURN server), 49152-49172 (TURN server UDP range)
git clone --branch v1.0-selfhost --depth 1 https://github.com/makesnosense/voice.git
cd voiceThis downloads the stable self-hosted version (v1.0-selfhost) without full git history.
cp .env.selfhost.example .env
nano .env # or use vim, micro, etc.Edit the following in .env:
DOMAIN=your-domain.com # your actual domain
EMAIL=your-email@example.com # for Let's Encrypt notifications
COTURN_SECRET= # generate with: openssl rand -base64 32To generate a strong secret:
openssl rand -base64 32If you don't mind using a setup script, this is the fastest way. (setup.sh is just two docker run commands)
bash setup.sh
docker compose up -dThat's it! ✨
Your voice chat is now running at https://your-domain.com
Load environment variables and get certificate:
source .env
docker run --rm -p 80:80 \
-v voice_certbot-conf:/etc/letsencrypt \
certbot/certbot certonly --standalone \
--email ${EMAIL} \
--agree-tos --no-eff-email --non-interactive \
-d ${DOMAIN}You should see: Successfully received certificate
source .env
docker run --rm \
-v ./client:/app/client \
-v ./shared:/app/shared \
-v voice_client-dist:/app/dist \
-e VITE_TURN_SERVER_HOST=${DOMAIN} \
-e VITE_TURN_SERVER_PORT=${VITE_TURN_SERVER_PORT} \
-w /app/client \
node:alpine \
sh -c "npm ci && npm run build && cp -r dist/* /app/dist/"This takes 1-2 minutes. You should see: ✓ built in XXs
docker compose up -d- Install dependencies:
cd mobile && npm install - Download
google-services.jsonfrom Firebase Console - Place in
mobile/android/app/google-services.json
cd mobile
npm start # Start Metro bundler
npm run android # Run on Android devicecd mobile/android
./gradlew assembleDebug # Debug APK
./gradlew assembleRelease # Release APK (requires signing)