ICE Restart

ICE restart is a mechanism in WebRTC that triggers a fresh round of ICE negotiation on an existing PeerConnection without tearing down the session.

It is defined in IETF RFC 8445 (the current ICE specification) and exposed in the WebRTC API as RTCPeerConnection.restartIce().

Why ICE restart is needed

Once ICE completes, the chosen candidate pair stays in use for the duration of the call. That works until the network underneath changes. Common triggers:

  • A device switches from Wi-Fi to cellular (or between two Wi-Fi networks)
  • A NAT binding times out and a new public address is assigned
  • The ICE connection state moves to failed or disconnected
  • A TURN allocation expires or the relay path breaks

Without ICE restart, the only option in these situations is to drop the call and create a new PeerConnection from scratch. ICE restart lets the session recover in place, usually without the user noticing more than a short media freeze.

In many ways, the reasons to use ICE restart is to reduce the time to handle connection breakages that are temporary in nature, shaving a couple hundreds of milliseconds versus the alternative.

How ICE restart works

An ICE restart is essentially a fresh ICE session running inside the same PeerConnection:

  1. One side generates a new SDP offer with new ice-ufrag and ice-pwd values (the credentials that scope an ICE session)
  2. Both peers gather ICE candidates again from scratch, including fresh STUN reflexive candidates and TURN relay candidates
  3. Connectivity checks run against the new candidate pairs
  4. Once a working pair is nominated, media switches to the new path
  5. The old ICE session is discarded

During the restart, the existing media path continues to be used for as long as it is still working. If it is already broken, there is a gap until the new path is established.

Triggering an ICE restart

Before any of the code, be clear on one thing: an ICE restart is the application's call. The browser will not trigger one on its own. If your app does not know a restart is happening, it cannot tell the user, and a user staring at a frozen call with no feedback assumes your app is broken.

There are two ways to start an ICE restart from JavaScript:

  • pc.restartIce() - This approach marks the connection as needing an ICE restart, fires negotiationneeded, and the next createOffer() will include the restart flag automatically
  • pc.createOffer({ iceRestart: true }) - Works just as well, though restartIce() is preferred if you plan on using Perfect Negotiation patterns

A common pattern is to listen for ICE state changes and restart automatically, but which state you react to matters. failed is a dead end - the connection is not coming back on its own, so restarting there is safe but late. disconnected is the earlier warning: the path might recover by itself within a second or two, or it might not. A common heuristic is to wait out disconnected for about 2 seconds and restart only if it has not cleared:

let restartTimer;
pc.oniceconnectionstatechange = () => {
  if (pc.iceConnectionState === 'failed') {
    pc.restartIce();
  } else if (pc.iceConnectionState === 'disconnected') {
    restartTimer = setTimeout(() => {
      if (pc.iceConnectionState === 'disconnected') pc.restartIce();
    }, 2000);
  } else {
    clearTimeout(restartTimer);
  }
};

The 2 second figure is a heuristic, not a spec value - tune it to your traffic.

ICE restart vs full renegotiation

ICE restart is narrower than a full SDP renegotiation:

  • ICE restart replaces only the ICE credentials and candidates. Codecs, SDP media sections, and DTLS keys stay the same
  • Full renegotiation (calling createOffer() without the restart flag) can add or remove tracks, change codecs, or renegotiate media parameters

Restart is cheaper and faster because the DTLS handshake and SRTP keys are preserved. It is the right tool for pure connectivity recovery, not for media changes.

ICE restart vs opening a new PeerConnection

There is a second way to recover a broken connection that gets less attention than it deserves: throw the RTCPeerConnection away and build a new one. Detect the failure, close the old PeerConnection, open a new one, signal it, resume. It is not glamorous, and it is often the right answer - especially when the media server side is unreliable or outside your control.

The trade-off comes down to a handful of factors:

  • Media gap - an ICE restart usually recovers in under a second, because the existing path keeps carrying media until the new one is ready. A new PeerConnection is more like 1-3 seconds while DTLS and SRTP are set up from scratch
  • State complexity - a restart keeps your tracks, transceivers, and simulcast layers in place. A new PeerConnection resets all of it, so you re-add tracks and re-establish simulcast
  • Transport and keys - a restart preserves the DTLS handshake and SRTP keys. A new PeerConnection allocates a fresh transport and runs a new handshake
  • Server support - this is usually the deciding factor. A client-initiated ICE restart only works if the media server cooperates. Opening a new PeerConnection works everywhere, because every server already knows how to accept a new connection
  • Failure semantics - a restart is a clean, well-defined operation when it is supported. The new-PeerConnection route leans on your own failure detection and reconnection heuristics, which is messier but does not depend on anyone else implementing the restart path correctly

Rule of thumb: reach for restartIce() when you control the media server and know it handles restarts, and fall back to a fresh PeerConnection when you do not.

ICE restart and mobile networks

Mobile clients are the main reason ICE restart exists in practice. Network handovers (Wi-Fi to LTE, LTE to 5G, switching access points) change the local IP address and often invalidate NAT bindings. Applications that run long calls on mobile devices - especially call center and telehealth apps - should always wire up automatic ICE restart on failed, and consider restarting on disconnected for faster recovery.

Media servers and ICE restarts

For ICE restart to work when there is a media server in the path, the server itself has to support the logic. In an SFU or MCU the server is one of the two peers, so it has to play along - and this isn't always the case.

Before you build your reconnection logic on restartIce(), ask the SFU or CPaaS two questions:

  1. Will it accept a client-initiated ICE restart?
  2. Will it initiate one itself when it detects a dead path?

Plenty of servers answer no to one or both. A server that won't accept a client restart turns your restartIce() call into a no-op, and one that won't initiate its own restart leaves a server-detected failure unrecovered. ICE restart also isn't commonly found in VoIP media servers that focus on SIP connectivity. If you need it, confirm support before you design around it - and if it isn't there, the new-PeerConnection route above is your fallback.

Additional reading

Tsahi Levent-Levi

Tsahi Levent-Levi

Independent WebRTC analyst. 20+ years in telecom, 13 focused on WebRTC. Writes for developers and product teams who need to understand, not just implement, real-time communications.