Using ExpressTURN with Pion (Go)

Pure-Go WebRTC. Pass ICEServers in your webrtc.Configuration.

Standard configuration

// go get github.com/pion/webrtc/v4
package main

import "github.com/pion/webrtc/v4"

func newPC() (*webrtc.PeerConnection, error) {
  cfg := webrtc.Configuration{
    ICEServers: []webrtc.ICEServer{
      { URLs: []string{"stun:stun.expressturn.com:3478"} },
      {
        URLs: []string{
          "turn:relay1.expressturn.com:3478?transport=udp",
          "turn:relay1.expressturn.com:3478?transport=tcp",
          "turns:relay1.expressturn.com:443?transport=tcp",
        },
        Username:   "YOUR_EXPRESSTURN_USERNAME",
        Credential: "YOUR_EXPRESSTURN_PASSWORD",
      },
    },
    ICETransportPolicy: webrtc.ICETransportPolicyAll,
  }
  return webrtc.NewPeerConnection(cfg)
}

Force-relay testing

cfg.ICETransportPolicy = webrtc.ICETransportPolicyRelay

This forces every connection through TURN — useful for catching configuration mistakes early.

Server-authoritative game pattern

If you're running an authoritative game server (Pion is popular for this), the server's PeerConnection usually doesn't need TURN — it has a public address. Add TURN to the client's ICE config so connecting players get a relay path.

Embedded uses (IoT cameras, set-top boxes) are different — those endpoints sit behind NAT and absolutely need TURN.

Per-session credentials with HMAC

import (
  "crypto/hmac"
  "crypto/sha1"
  "encoding/base64"
  "fmt"
  "time"
)

func turnCreds(secret, userID string) (username, credential string) {
  expiry := time.Now().Unix() + 3600
  username = fmt.Sprintf("%d:%s", expiry, userID)
  h := hmac.New(sha1.New, []byte(secret))
  h.Write([]byte(username))
  credential = base64.StdEncoding.EncodeToString(h.Sum(nil))
  return
}

See shared-secret examples in other languages.

Common pitfalls

  • Older Pion versions: Pre-v3 used v2/webrtc. The API for ICEServers is largely the same, but check your import paths.
  • Goroutine leaks: Always defer pc.Close() on connection failure paths.
  • Missing TLS-443: Without it, Pion clients on restrictive corporate networks fail.

Related: Janus recipe · TURN for multiplayer games · TURN for IP cameras

Done.

Get Free TURN Credentials