Overview
What the lifecycle service does
Pulling power on a running cluster leaves databases mid-write and pods wedged on nodes that never drained. The lifecycle service does that work deliberately: it records how many replicas every workload has, scales them to zero in dependency order, drains the nodes, then stops kubelet. On the next boot it reverses all of it.
Two systemd units, both backed by scripts in /opt/netlinq/installation/:
netlinq-shutdown.serviceMaster: syncs state, scales apps to 0, drains nodes, stops local services. Worker: drains itself, stops local services. Runs on systemctl stop or OS shutdown, via shutdown_netlinq.sh.netlinq-startup.serviceMaster: uncordons every node and restores the pending replica snapshot. Worker: uncordons itself and waits for the master. Runs automatically after boot, via startup_netlinq.sh.Day to day nothing calls these by hand — a normal sudo reboot or sudo shutdown -h now runs the shutdown hook, and the restore runs itself on the way back up. To drive them directly:
# scale the platform down and drain the nodes $ sudo systemctl stop netlinq-shutdown.service # bring it back: uncordon and restore every replica count $ sudo systemctl restart netlinq-startup.service # re-arm the shutdown hook for the next reboot $ sudo systemctl start netlinq-shutdown.service
restart, not start?netlinq-startup.service has RemainAfterExit=yes, so it is still active from boot and start does nothing. restart is what re-runs the restore — it is what the installer itself uses.oneshot unit never runs its ExecStop — the next reboot would be an uncontrolled one. Starting it is free: its ExecStart is /bin/true.$ sudo /opt/netlinq/installation/shutdown_netlinq.sh $ sudo /opt/netlinq/installation/startup_netlinq.sh
netlinq-shutdown.service carried ExecStart=startup_netlinq.sh, so on those hosts systemctl start netlinq-shutdown.service is what scaled everything back up. Check which build you are on with systemctl cat netlinq-shutdown.service: ExecStart=/bin/true means the restore lives in netlinq-startup.service.oneshot unit only runs its ExecStop while it is active, so when both lived on one unit a single failed restore silently left the machine with no controlled shutdown at all. The shutdown unit now has ExecStart=/bin/true — arming the hook cannot fail.Step 1
Turn it on
The shipped netlinq_onprem_install.vars sets enable_netlinq_shutdown_service=true, so a stock install enables and starts both units for you. Drop the variable entirely and the installer falls back to leaving them off.
Leave the variable at true
The installer enables and starts both units, and applies the host recovery settings first. Set it to "false" if you do not want them armed.
Enable them by hand
On a cluster that is already running, enable both units on every node.
enable_netlinq_shutdown_service="true"
$ sudo systemctl enable --now netlinq-startup.service netlinq-shutdown.service
Step 2
Prepare an existing cluster
Both paths set the two kubelet shutdown grace periods to 0s, so kubelet hands the shutdown over instead of running its own partial eviction, and write persistent IPv6 settings for physical, bond, bridge and VLAN uplinks.
Single node
$ sudo bash installation/configure_host_recovery.sh --all
It restarts kubelet only if the config actually changed. Run it before enabling the units.
Multi-node cluster
Install and enable the units on every node first. Then run the migration from a control-plane host where kubectl get nodes works for your user.
$ cd installation $ source kubespray-venv/bin/activate $ kubectl get nodes $ ansible-playbook -i kubespray-repo/inventory/mycluster/hosts.yaml \ migrate_kubelet_shutdown.yml --ask-become-pass
--ask-become-pass.kube_override_hostname for that host.installation/upgrade_kubespray.sh run keeps both 0s values in the regenerated inventory.Reference
What happens at shutdown
On a master node, in this order:
replicas.json, so any manual scaling done during operations survives the reboot.CrashLoopBackOff or other unhealthy states. It proceeds anyway, and says so in the journal.minReplicas > 0 would pause the scale-down at its own floor. If the backup fails, the HPAs are left in place.strimzi-cluster-operator and netlinq-cluster-operator are scaled to zero before anything they manage, or they reconcile their workloads straight back up.shutdown_order stop in that order. Everything else follows in whatever order the API returns, with a warning in the journal naming each one.keycloak is a Deployment but belongs in the order because it must stop after the applications that authenticate against it and before postgresql, the database it depends on.The unit allows 1800s (TimeoutStopSec) while the script budgets 1500s for itself, so it winds down on its own terms instead of being killed part-way through a drain.
Reference
What happens after boot
The restore is allowed to take a while: up to 600s waiting on databases and another 600s on the remaining workloads, inside a 2400s TimeoutStartSec. On failure it retries up to three times in three hours, then stops. The unit is deliberately not masked to success — a failed restore stays visible in systemctl status and no longer affects the shutdown hook.
Reference
State files
replica_counts_snapshot_*.txtLive replica counts captured before the first scale-down. A completion marker keeps a partial capture from ever being replayed. Startup consumes it after a successful restore.replicas.jsonShutdown order per namespace, the operator deployment list, and the counts kept for reference.Both live in /opt/netlinq/installation/. Until the snapshot is consumed, a later shutdown merges it with live counts, so a workload still sitting at zero keeps its pending count instead of being recorded as zero.
{ "netlinq": { "shutdown_order": [ "diesel", "elastic", "keycloak", "kibana", "mongo-tenant", "postgresql" ] }, "kafka": { "operator_deployments": [ "strimzi-cluster-operator", "netlinq-cluster-operator" ] } }
A new database with dependents belongs in .netlinq.shutdown_order. Leave it out and it stops in arbitrary order among the other workloads — the journal warns by name when that happens.
Commit the current state without shutting anything down:
$ sudo /opt/netlinq/installation/shutdown_netlinq.sh --sync-only
Fallback
Scaling down by hand
This is the old runbook — what the service now does for you. Use it when the units are not installed, or when you want the platform down without stopping the machine.
kubectl scale does not remember what a workload was set to.$ kubectl -n netlinq get deploy,sts \ -o custom-columns=KIND:.kind,NAME:.metadata.name,REPLICAS:.spec.replicas \ > replicas-before.txt
$ kubectl -n kafka scale deploy strimzi-cluster-operator \ netlinq-cluster-operator --replicas=0
$ kubectl -n netlinq get hpa -o yaml > hpa-backup.yaml $ kubectl -n netlinq delete hpa --all
# order: diesel, elastic, keycloak, kibana, mongo-tenant, postgresql $ kubectl -n netlinq get deploy,sts # check each one's kind $ kubectl -n netlinq scale deployment/diesel --replicas=0 $ kubectl -n netlinq rollout status deployment/diesel --timeout=120s
$ kubectl -n netlinq scale deploy --all --replicas=0 $ kubectl -n netlinq scale sts --all --replicas=0
To bring it back, reverse the order: databases first, then the applications, then the operators, then re-apply hpa-backup.yaml. Restore each workload to the count in replicas-before.txt, not to 1.
Step 3
Turn it off
Disabling the shutdown unit makes the next reboot an ordinary one: no scale-down, no drain, no snapshot, nothing to restore.
$ sudo systemctl disable --now netlinq-shutdown.service netlinq-startup.service
disable --now, not a plain stop. Stopping netlinq-shutdown.service is exactly what runs its ExecStop — a bare systemctl stop triggers a full controlled shutdown of the cluster right then.Set enable_netlinq_shutdown_service="false" in the vars file as well, so a later install or upgrade does not quietly arm it again.
Help
Verify and read the logs
$ systemctl status netlinq-shutdown.service netlinq-startup.service kubelet $ cat /opt/netlinq/installation/replicas.json
Both scripts log to the systemd journal, so the record survives the reboot they were running for:
$ journalctl -u netlinq-shutdown.service -f $ journalctl -u netlinq-startup.service -b
The startup unit is in failed state›
sudo systemctl reset-failed netlinq-startup.service followed by sudo systemctl start netlinq-startup.service.A workload came back at the wrong replica count›
/opt/netlinq/installation/, and whether an operator reconciled it afterwards. The snapshot is only consumed after a successful restore.The journal warns about an unordered workload›
.netlinq.shutdown_order in replicas.json, then run shutdown_netlinq.sh --sync-only.