A deployment job connects, copies its artifact, and reports success. The tunnel it opened can still be up when the next job starts, and the route it changed belongs to the whole host. A build result describes the command, not the state it left behind. Treat the VPN as a resource with a lifetime, not as one line at the start of a stage.

What else runs on this host

A Jenkins node can run several executors at once, and a job that changes host routes, resolver settings or a system-wide connection changes them for everyone. Whether another job notices depends on the route and filtering policy, not on a general rule about VPNs. Repository access, artifact access and the control channel are separate requirements the runner still needs. Add those to the deployment destination list before choosing a full tunnel.

If the control channel would cross the tunnel, the design has to say so deliberately. Discovering it during a rollback is not a plan. Write the two lists down: what the job must reach privately, and what the runner must reach regardless.

Does this job need private access at all?

Ask before adding the tunnel whether the build stage needs it. A private deployment agent can fetch an approved artifact and perform the deployment, which keeps the general build stage out of the private network. That split is usually cheaper than teaching every build step to route through a tunnel.

Choose the boundary from the workflow rather than from habit. If the answer is no, the rest of this page is unnecessary, which is the best outcome available.

The state the next job inherits

Describe what should be true before the job, while it holds private access, and after it ends, including cancellation. The end state is the one most often left implicit, and it is the one the next job inherits. Write it next to the pipeline instead of leaving it in someone's memory.

MomentWhat proves itWhat leaks if it is skipped
Job start: pre-flight route checkThe route table shows no tunnel from an earlier job, and the profile list is emptyA stale route sends deployment traffic into a dead tunnel
Job end: disconnect and shredThe profile is down and the temporary key file is goneThe next job inherits an open tunnel and a usable credential
Cancelled job: reboot or disposalThe host is replaced or rebooted before the next job startsA killed process leaves the tunnel up with no owner

A dedicated or disposable agent makes those boundaries easier to enforce. A container or namespace helps only if its network and privilege arrangement actually isolates the change.

Stop the connection, not the program

Use the pipeline's finalization path to close the connection this job started and remove its temporary credentials. Do not match processes by program name on a host that other jobs use; stop the specific connection instead. The two commands below are the whole cleanup, and neither depends on finding a process.

vpn-cli disconnect --profile "$PROFILE"
rm -f "$RUNNER_TEMP/client.key"

Finalization is not isolation: a runner that dies keeps the tunnel it opened, and a process that is force-stopped skips the block entirely. That is why the cleanup command alone cannot be the whole design. Here is how it fails in practice: a deployment job is cancelled at the copy step, the finalization block never runs, and the next job on the same host starts with a live tunnel and a route that points at a network it cannot reach. The failure looks like a DNS problem in the next build, hours after the job that caused it finished.

Record the profile name and the credential path in the job output. A bare disconnect command with no identifier is hard to audit afterwards. The log line is also what proves the block ran when a later investigation asks.

Give the job a host nobody else uses

Disposable infrastructure removes the inheritance problem rather than managing it. A fresh agent per job starts with no route, no profile and no credential from anyone else, and its destruction cleans up whatever the job left. That costs more per build, and it is the honest price of a boundary that does not depend on cleanup code. For jobs that touch production networks, it is usually the cheaper option once incidents are counted.

Where a shared runner has to stay, test failure and cancellation as carefully as the successful copy. The test should show what the next job would see if this one were cancelled halfway. A job is done when its access and temporary state have ended, not when the copy command returns zero.

Artitgels cun il medem tema