Run a local process as if it were inside your cluster: prefix any command with plug and it resolves the cluster DNS and reaches the cluster services like any workload of the stack - no code change, no proxy configuration in your app. One tiny agent container on the cluster side, one static CLI on yours. Docker, Swarm, Kubernetes and OpenShift.

Continuous deployment & GitOps

If your cluster is driven by a continuous-deployment controller - Argo CD, Flux, Rancher Fleet, Crossplane - there is one interaction with plug worth knowing about. It affects -s only (publishing a name into the cluster); consuming cluster services is untouched, and everything here applies to Kubernetes and Swarm alike.

Why the two disagree

A CD controller's whole job is to keep the cluster equal to what a repository declares. It watches the live objects, and whatever differs from the declared state is drift - something to undo. That is the feature you deployed it for.

plug's -s does the opposite on purpose: for the lifetime of a session it modifies a live object so a cluster name points at your machine instead of the deployed workload. On Kubernetes that means patching the Service's spec.selector and spec.ports; on Swarm, the DNS alias. The controller sees exactly what it is built to revert, and reverts it - usually within minutes.

Nothing errors on either side. plug's patch is valid and applies cleanly; the controller's restore is valid too. The name simply stops pointing at you, quietly, some time after the session started.

How it shows up

plug proves the path end to end in the background and warns when the name never carried traffic. Which warning you get depends on whether the deployed workload is running:

WarningWhat happened
is not reachable inside the cluster The workload is not deployed. The restore left the Service selecting a workload that does not exist - no endpoints, and connections time out.
is answered by something else The workload is running. The name went back to it, so requests are served by the deployed version and your local process never sees one.

The second is the one to watch for: nothing looks broken. The application answers, the page loads - it is simply not your code answering. If a session seems to work for a few minutes and then stops reflecting your changes, this is the first thing to check.

Confirming it

Look at the annotations and labels on the object plug tried to take over (kubectl describe svc <name>, or d on the Service in k9s):

ControllerWhat to look for
Argo CDargocd.argoproj.io/tracking-id (annotation) or app.kubernetes.io/instance (label)
Fluxkustomize.toolkit.fluxcd.io/name, helm.toolkit.fluxcd.io/name
Rancher Fleetfleet.cattle.io/bundle-name
Crossplane, operatorscrossplane.io/composite, or an ownerReferences entry with controller: true
Two false leads. app.kubernetes.io/managed-by: Helm on its own means nothing here - Helm acts on an upgrade and does not reconcile, so a chart-deployed Service is perfectly takeable. And app.kubernetes.io/instance is a standard Kubernetes label that Helm sets on everything; it only indicates Argo CD when Argo is configured to track by label, so the annotation is the reliable signal.

Argo CD

Tell the Application not to compare the two fields plug changes. Everything else about the Service keeps being reconciled:

spec:
  ignoreDifferences:
    - group: ""
      kind: Service
      name: my-service
      namespace: my-namespace
      jsonPointers:
        - /spec/selector
        - /spec/ports

This lives in the Application object (a CRD, usually in the argocd namespace) - not in the chart. On a cluster where plug is used routinely, set it once for every Service in the argocd-cm ConfigMap instead of Application by Application. The empty prefix in the key is how the core API group is spelled:

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  resource.customizations.ignoreDifferences._Service: |
    jsonPointers:
      - /spec/selector
      - /spec/ports

For a one-off, turning off Self Heal (App Details → Sync Policy) also works and needs no manifest change - but it suspends drift correction for the whole Application, it is easy to forget to turn back on, and a Git push or a manual sync re-applies the Service anyway.

Flux

Flux is the one controller that takes the instruction on the resource itself, so here the chart is the right place:

metadata:
  annotations:
    kustomize.toolkit.fluxcd.io/reconcile: disabled

Note this is broader than Argo's ignoreDifferences: it stops reconciling the whole object, not two fields. The same annotation can be applied through the Kustomization with spec.patches if you would rather not carry it in the chart.

Rancher Fleet

Fleet compares with patches declared in the bundle's fleet.yaml:

diff:
  comparePatches:
    - apiVersion: v1
      kind: Service
      name: my-service
      namespace: my-namespace
      operations:
        - op: remove
          path: /spec/selector
        - op: remove
          path: /spec/ports

Crossplane and bespoke operators

When a Service is managed as a Crossplane Object, switching that resource to managementPolicies: ["Observe"] stops it being re-applied. An operator written in-house rarely offers an equivalent: there, the options are to suspend the operator for the duration, or to serve a name it does not own.

After a session

Ending a session properly (Ctrl-C) is what restores the object, from a receipt plug stored on it. That matters more than usual here, because the extra selector key plug adds is not removed by the controller - it only manages fields it knows about. A session killed with kill -9 can therefore leave a Service selecting both its own workload and plug, which matches no pod at all: the name then answers nobody, with or without plug running. If that happens, remove the leftover app: plug key from the selector by hand.

Details of what -s does per orchestrator are on the Kubernetes and Swarm pages; other symptoms are covered under Troubleshooting.