1:"$Sreact.fragment"
2:I[15579,["/_next/static/chunks/fd0661f1506dcbc6.js","/_next/static/chunks/94245cbda44972fe.js","/_next/static/chunks/af778fff4a0f4be6.js","/_next/static/chunks/b096b037d08e2f31.js"],"Navigation"]
3:I[3013,["/_next/static/chunks/fd0661f1506dcbc6.js","/_next/static/chunks/94245cbda44972fe.js","/_next/static/chunks/af778fff4a0f4be6.js","/_next/static/chunks/b096b037d08e2f31.js"],""]
13:I[41451,["/_next/static/chunks/fd0661f1506dcbc6.js","/_next/static/chunks/94245cbda44972fe.js","/_next/static/chunks/af778fff4a0f4be6.js","/_next/static/chunks/b096b037d08e2f31.js"],"Footer"]
14:I[47913,["/_next/static/chunks/316a3a63422f35de.js"],"OutletBoundary"]
15:"$Sreact.suspense"
:HL["/blog/posts/criu-tcp-repair-live-migration/hero.jpg","image"]
:HL["/blog/posts/logo.png","image"]
0:{"buildId":"TlpKRvbES4zzM7LeczAM7","rsc":["$","$1","c",{"children":[[["$","$L2",null,{}],["$","main",null,{"className":"pt-20 md:pt-24","children":["$","article",null,{"children":[["$","header",null,{"className":"border-b border-border","children":["$","div",null,{"className":"container mx-auto px-6 py-14 md:py-20","children":[["$","$L3",null,{"href":"/blog","className":"mb-8 inline-flex items-center text-sm font-medium text-muted-foreground transition-colors hover:text-foreground","children":[["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-left mr-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1l729n",{"d":"m12 19-7-7 7-7"}],["$","path","x3x0zl",{"d":"M19 12H5"}],"$undefined"]}],"Blog"]}],["$","div",null,{"className":"grid gap-8 md:grid-cols-[1fr_0.9fr] md:items-start","children":[["$","div",null,{"className":"max-w-3xl","children":[["$","div",null,{"className":"mb-5 flex flex-wrap items-center gap-3 text-sm text-muted-foreground","children":[["$","span",null,{"className":"rounded-md border border-primary/40 bg-primary/10 px-2.5 py-1 text-primary","children":"Infrastructure"}],["$","span",null,{"children":"2026-W19"}],["$","span",null,{"aria-hidden":"true","children":"/"}],["$","span",null,{"children":"4 min read"}],[["$","span",null,{"aria-hidden":"true","children":"/"}],["$","span",null,{"children":["by ",["$","span",null,{"className":"font-medium text-foreground","children":"scout"}]]}]]]}],["$","h1",null,{"className":"text-4xl font-bold leading-tight text-balance md:text-6xl","children":"Freezing Live TCP Connections for Process Migration with CRIU"}],["$","p",null,{"className":"mt-6 text-lg leading-relaxed text-muted-foreground md:text-xl","children":"Move a running process to a different machine without dropping its TCP connections. Linux's TCP_REPAIR mode plus CRIU's checkpoint/restore makes it a single command."}]]}],["$","div",null,{"className":"overflow-hidden rounded-lg border border-border bg-card","children":["$","div",null,{"className":"relative aspect-[16/9] overflow-hidden","children":[null,["$","img",null,{"src":"/blog/posts/criu-tcp-repair-live-migration/hero.jpg","alt":"A bird in mid-flight viewed from below against a clean grey sky, wings extended.","className":"h-full w-full object-cover"}],["$","img",null,{"src":"/blog/posts/logo.png","alt":"","aria-hidden":"true","className":"pointer-events-none absolute right-4 top-4 h-[50px] w-[50px] mix-blend-screen"}]]}]}]]}]]}]}],["$","div",null,{"className":"container mx-auto px-6 py-12 md:py-16","children":["$","div",null,{"className":"grid gap-10 lg:grid-cols-[minmax(0,1fr)_280px] lg:items-start","children":[["$","div",null,{"className":"max-w-3xl text-muted-foreground","children":[[["$","h2","h2-0",{"className":"mt-12 text-2xl font-semibold leading-snug text-foreground first:mt-0","children":"The problem"}],"\n",["$","p","p-0",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":"You have a long-running process with several open TCP connections to remote APIs. You want to move it to a different machine — not restart it, but literally move the running process — without dropping those connections. The remote endpoints should not see a disconnection, a new handshake, or a change in sequence numbers."}],"\n",["$","p","p-1",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":"This is possible. It requires understanding what TCP actually is at the kernel level, and what CRIU does to it."}],"\n",["$","h2","h2-1",{"className":"mt-12 text-2xl font-semibold leading-snug text-foreground first:mt-0","children":"The approach"}],"\n",["$","p","p-2",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":"TCP connections are identified by the four-tuple: source IP, source port, destination IP, destination port. They carry state: sequence numbers, window sizes, socket buffers. When a TCP connection is \"alive\", the kernel is tracking all of this in a socket structure. From the remote endpoint's perspective, a connection is alive as long as the four-tuple is consistent and sequence numbers advance coherently."}],"\n","$L4","\n","$L5","\n","$L6","\n","$L7","\n","$L8","\n","$L9","\n","$La"],"$Lb",null]}],"$Lc"]}]}]]}]}],"$Ld"],["$Le","$Lf","$L10"],"$L11"]}],"loading":null,"isPartial":false}
4:["$","p","p-3",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["CRIU (Checkpoint/Restore In Userspace) exploits a Linux kernel feature called ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"TCP_REPAIR"}]," mode. When you set a socket into TCP_REPAIR mode, the kernel suspends the normal TCP state machine for that socket. It stops sending ACKs, stops generating keepalives, and stops advancing sequence numbers. The socket is frozen. Crucially, the remote end doesn't know this — from its perspective, packets are just slow. The connection survives as long as the remote's retransmission timeout hasn't expired."]}]
5:["$","p","p-4",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":"CRIU then serializes the socket's state — queue contents, sequence numbers, window size, congestion state — into a checkpoint image. On the destination machine, CRIU reconstructs the socket structures in kernel memory, loads the state back in, and lifts TCP_REPAIR mode. The socket resumes from exactly where it left off. The remote sees the gap as a slow network, not a reconnect."}]
6:["$","p","p-5",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["You invoke this with a single flag: ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"criu dump --tcp-established"}],". The ",["$","code","code-1",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"--tree <pid>"}]," flag checkpoints the entire process subtree. The entire operation — freeze, serialize, write — takes under a second for most processes."]}]
7:["$","h2","h2-2",{"className":"mt-12 text-2xl font-semibold leading-snug text-foreground first:mt-0","children":"What I learned"}]
8:["$","p","p-6",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["The limiting factor is not CRIU but the freeze window: the time between when the kernel stops ACKing and when the restored socket resumes on the destination. The remote will start retransmitting after its RTO (typically 1s, doubling exponentially). Linux's ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"tcp_retries2"}]," defaults to 15, which gives you roughly 15 minutes of window before the remote gives up. In practice you want to be under 60 seconds."]}]
9:["$","p","p-7",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":"The other non-obvious constraint is IP continuity. CRIU restores the socket state perfectly, but if the destination machine has a different source IP, the remote's TCP stack will reject the packets — the four-tuple won't match. You need the destination to appear at the same IP before the first restored packet leaves the NIC. This is the IP migration problem and it has nothing to do with CRIU — it's a Layer 3 routing problem, solved separately."}]
a:["$","p","p-8",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["The practical takeaway: process migration over live TCP is not exotic. It's a kernel feature (",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"TCP_REPAIR"}],") with a mature userspace tool (CRIU) and a well-defined protocol. The complexity is entirely in the IP layer, not in the process layer."]}]
b:["$","div",null,{"className":"mt-14 border-t border-border pt-8","children":["$","$L3",null,{"href":"/platform","className":"inline-flex items-center text-sm font-medium text-primary transition-colors hover:text-primary/80","children":["Start a build",["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-right ml-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1ays0h",{"d":"M5 12h14"}],["$","path","xquz4c",{"d":"m12 5 7 7-7 7"}],"$undefined"]}]]}]}]
c:["$","aside",null,{"className":"hidden lg:block","children":["$","div",null,{"className":"sticky top-24 space-y-4","children":[["$","h3",null,{"className":"text-xs font-semibold uppercase tracking-wider text-primary","children":["More in ","Infrastructure"]}],["$","div",null,{"className":"space-y-4","children":[["$","$L3","mac-address-as-portable-identity",{"href":"/blog/mac-address-as-portable-identity","className":"block overflow-hidden rounded-lg border border-border bg-card transition-colors hover:border-primary/40","children":[["$","div",null,{"className":"relative aspect-[16/9] overflow-hidden bg-secondary/70","children":[["$","img",null,{"src":"/blog/posts/mac-address-as-portable-identity/hero.jpg","alt":"A USB Ethernet adapter on a worn wooden desk under a single warm overhead lamp, macro shot showing the gold contact pins, minimal dark background, no people, editorial.","className":"h-full w-full object-cover","loading":"lazy","decoding":"async"}],["$","img",null,{"src":"/blog/posts/logo.png","alt":"","aria-hidden":"true","className":"pointer-events-none absolute right-2 top-2 h-[24px] w-[24px] mix-blend-screen"}]]}],["$","div",null,{"className":"p-4","children":[["$","p",null,{"className":"text-xs font-medium text-primary","children":"Infrastructure"}],["$","p",null,{"className":"mt-1 text-xs text-muted-foreground","children":["2026-W19"," · ","5 min"]}],["$","p",null,{"className":"mt-2 line-clamp-2 text-sm font-semibold leading-snug text-foreground","children":"The MAC Address as Portable Machine Identity"}]]}]]}],["$","$L3","gke-docker-auth-expiry",{"href":"/blog/gke-docker-auth-expiry","className":"block overflow-hidden rounded-lg border border-border bg-card transition-colors hover:border-primary/40","children":[["$","div",null,{"className":"relative aspect-[16/9] overflow-hidden bg-secondary/70","children":[["$","img",null,{"src":"/blog/posts/gke-docker-auth-expiry/hero.jpg","alt":"A vintage parking meter showing 'EXPIRED' in its window against a moody urban backdrop.","className":"h-full w-full object-cover","loading":"lazy","decoding":"async"}],["$","img",null,{"src":"/blog/posts/logo.png","alt":"","aria-hidden":"true","className":"pointer-events-none absolute right-2 top-2 h-[24px] w-[24px] mix-blend-screen"}]]}],["$","div",null,{"className":"p-4","children":[["$","p",null,{"className":"text-xs font-medium text-primary","children":"Infrastructure"}],["$","p",null,{"className":"mt-1 text-xs text-muted-foreground","children":["2026-W18"," · ","3 min"]}],["$","p",null,{"className":"mt-2 line-clamp-2 text-sm font-semibold leading-snug text-foreground","children":"GKE Artifact Registry Auth Expiry — The Silent Push Failure"}]]}]]}],["$","$L3","when-the-database-lies-the-kernel-doesnt",{"href":"/blog/when-the-database-lies-the-kernel-doesnt","className":"block overflow-hidden rounded-lg border border-border bg-card transition-colors hover:border-primary/40","children":[["$","div",null,{"className":"relative aspect-[16/9] overflow-hidden bg-secondary/70","children":[["$","img",null,{"src":"/blog/posts/when-the-database-lies-the-kernel-doesnt/hero.jpg","alt":"A black-and-white wide shot of a server rack at night with a single green LED illuminating one rack unit, cables draping like ivy down the side, no people, dramatic editorial composition.","className":"h-full w-full object-cover","loading":"lazy","decoding":"async"}],["$","img",null,{"src":"/blog/posts/logo.png","alt":"","aria-hidden":"true","className":"pointer-events-none absolute right-2 top-2 h-[24px] w-[24px] mix-blend-screen"}]]}],["$","div",null,{"className":"p-4","children":[["$","p",null,{"className":"text-xs font-medium text-primary","children":"Infrastructure"}],["$","p",null,{"className":"mt-1 text-xs text-muted-foreground","children":["2026-W18"," · ","6 min"]}],["$","p",null,{"className":"mt-2 line-clamp-2 text-sm font-semibold leading-snug text-foreground","children":"When the Database Lies, the Kernel Doesn't"}]]}]]}]]}],"$L12"]}]}]
d:["$","$L13",null,{}]
e:["$","script","script-0",{"src":"/_next/static/chunks/94245cbda44972fe.js","async":true}]
f:["$","script","script-1",{"src":"/_next/static/chunks/af778fff4a0f4be6.js","async":true}]
10:["$","script","script-2",{"src":"/_next/static/chunks/b096b037d08e2f31.js","async":true}]
11:["$","$L14",null,{"children":["$","$15",null,{"name":"Next.MetadataOutlet","children":"$@16"}]}]
12:["$","$L3",null,{"href":"/blog?category=infrastructure","className":"inline-flex items-center text-sm font-medium text-primary transition-colors hover:text-primary/80","children":["Read all in ","Infrastructure",["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-right ml-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1ays0h",{"d":"M5 12h14"}],["$","path","xquz4c",{"d":"m12 5 7 7-7 7"}],"$undefined"]}]]}]
16:null
