Remote
Use kind: ControlPlane to deploy Datasance PoT across one or more SSH hosts with Edgelet. The kind name is ControlPlane. Global settings (auth, database, images, TLS policy) are declared once. Each host is an entry under spec.controllers[].
Use this kind for bare metal or VMs, including a multi-host layout where the first system Edgelet node is the default router and the NATS hub. Additional hosts run interior system Edgelet nodes. See Multi-controller HA. Add a later host with Controller add-on.
The install steps are in Remote control plane. Prepare SSH and sudo with Prepare your remote hosts.
What deploy -f does
potctl deploy -f remote-controlplane.yaml -n prod
potctl does the following.
On every host, in parallel
- Check SSH (reachable host, key, user).
- Install Edgelet (native or container, from the system Edgelet node config).
- When
spec.airgaporcontrollers[].airgapis set, stage images and the Edgelet binary. That host then needssystemAgent.config.arch. - When
spec.controller.packagehas a registry, username, and password, write a private registry manifest on Edgelet. - Translate the global spec plus this
controllers[]row intoapiVersion: edgelet.iofog.org/v1,kind: ControlPlane. - Run
edgelet deploy -fon that host and poll until the Controller container is running.
No Controller API login happens until every host finishes this phase, or one of them fails.
Once, after every host succeeds
- Wait for the Controller API at
spec.endpointorcontroller.publicUrl. - When
auth.modeisembedded, log in with the bootstrap user and createiofogUserif it is missing. - When a private Controller image was used, register that registry on the Controller.
- When any of
routerSiteCA,routerLocalCA,natsSiteCA, ornatsLocalCAis set, upload those CAs through the Controller API. See Securing a remote control plane. - Fill
consoleUrlon the stored spec when it is still empty.
Then, one system Edgelet node at a time, in controllers[] order
Index 0 is the default router and the NATS hub (upstreamRouters and upstreamNatsServers empty). Later indexes attach to default-router and default-nats-hub. Details, database rules, and failure cases are on Multi-controller HA.
| Scope | Fields |
|---|---|
| Global (once) | endpoint, ca, iofogUser, controller, database, auth, routerSiteCA, routerLocalCA, natsSiteCA, natsLocalCA, systemMicroservices, nats, events, vault, tls, airgap |
Each controllers[] row | name, host, ssh, systemAgent (required), optional tls override, optional airgap |
Prerequisites:
- Passwordless sudo on the remote hosts, for the Edgelet install scripts.
- SSH key access:
ssh.user,ssh.keyFile, optionalssh.port(default 22). - A
systemAgentblock on every controller.{}is allowed. potctl fills defaults when config is omitted, except airgap, which requiresarch. endpointorcontroller.publicUrl, so the CLI can wait for the API and configure Edgelet nodes.
Per-host translation merges the global spec with that controllers[] entry. controllers[].tls overrides global spec.tls for that host only. The Edgelet manifest shape is documented in Edgelet control plane.
Adding a host later with kind: Controller needs a full remote control plane record in namespace config (including auth and controller). Get that from the initial deploy -f, or from connect -f with the same full file. A URL-only connect does not store enough for the add-on.
Examples
Single host. One controller may use Edgelet SQLite. Leave database unset.
apiVersion: datasance.com/v3
kind: ControlPlane
metadata:
name: edge-prod
spec:
endpoint: https://203.0.113.10:51121
iofogUser:
controller:
publicUrl: https://203.0.113.10:51121
consoleUrl: https://203.0.113.10
logLevel: info
package:
image: ghcr.io/datasance/controller:3.9.0
auth:
mode: embedded
bootstrap:
username: admin
password: "RemoteTest12!"
systemMicroservices:
router:
amd64: ghcr.io/datasance/router:3.9.0
arm64: ghcr.io/datasance/router:3.9.0
arm: ghcr.io/datasance/router:3.9.0
riscv64: ghcr.io/datasance/router:3.9.0
nats:
amd64: ghcr.io/datasance/nats:2.15.0
arm64: ghcr.io/datasance/nats:2.15.0
arm: ghcr.io/datasance/nats:2.15.0
riscv64: ghcr.io/datasance/nats:2.15.0
nats:
enabled: true
events:
auditEnabled: true
retentionDays: 14
controllers:
- name: ctrl-1
host: 203.0.113.10
ssh:
user: ubuntu
keyFile: ~/.ssh/prod.pem
port: 22
systemAgent:
config:
arch: amd64
host: 203.0.113.10
Two hosts need an external database. Index 0 is the default router and the NATS hub. Index 1 is an interior system Edgelet node.
apiVersion: datasance.com/v3
kind: ControlPlane
metadata:
name: edge-ha
spec:
endpoint: https://controller.example.com
iofogUser:
controller:
publicUrl: https://controller.example.com
consoleUrl: https://console.example.com
logLevel: info
package:
image: ghcr.io/datasance/controller:3.9.0
auth:
mode: embedded
bootstrap:
username: admin
password: "RemoteTest12!"
database:
provider: postgres
host: postgres.internal.example.com
port: 5432
user: controller
password: secret
databaseName: controller
ssl: true
systemMicroservices:
router:
amd64: ghcr.io/datasance/router:3.9.0
arm64: ghcr.io/datasance/router:3.9.0
arm: ghcr.io/datasance/router:3.9.0
riscv64: ghcr.io/datasance/router:3.9.0
nats:
amd64: ghcr.io/datasance/nats:2.15.0
arm64: ghcr.io/datasance/nats:2.15.0
arm: ghcr.io/datasance/nats:2.15.0
riscv64: ghcr.io/datasance/nats:2.15.0
nats:
enabled: true
controllers:
- name: ctrl-primary
host: 10.0.1.10
ssh:
user: ubuntu
keyFile: ~/.ssh/prod.pem
systemAgent:
config:
arch: amd64
host: 10.0.1.10
- name: ctrl-secondary
host: 10.0.1.11
ssh:
user: ubuntu
keyFile: ~/.ssh/prod.pem
systemAgent:
config:
arch: amd64
host: 10.0.1.11
Ordering, airgap, and what happens when a host fails: Multi-controller HA. Bring-your-own CAs: Securing a remote control plane.
Networking
There is no operator and no Kubernetes ConfigMap named iofog-router or iofog-nats-config for the hub. The Controller runs on an Edgelet node. That node is the system Edgelet node, and it is the default router and the NATS hub.
Local uses the same path. Kubernetes hubs are a different path: Kubernetes.
Edgelet registers the Controller on the system node with POST /api/v3/agent/controller/register. The call is accepted only when the Edgelet node is isSystem. The Controller stores a system microservice named controller in application system-{agentName}.
On an empty remote cluster, creating the first Edgelet node promotes it to isSystem, routerMode: interior, and natsMode: server, including when the request asked for another role. potctl does this through spec.controllers[0].systemAgent during deploy, after the Controller API is up.
The Controller then provisions the router and nats system microservices on that same node:
- The router is marked default when no default router exists yet (
default-router). - The NATS server is marked hub when no hub exists yet (
default-nats-hub).
Later Edgelet nodes attach to them. A system Edgelet node stays routerMode: interior and natsMode: server. An interior router that still has downstream routers cannot switch to edge.
The router container on this node uses host networking, so ports 45671 and 55671 bind on the host. The NATS container does not.
Certificate names are per Edgelet node (router-site-server-{agentName}, nats-server-{agent}). They are not the unsuffixed hub names the operator uses on Kubernetes (router-site-server, nats-site-server).
The Controller creates router-site-ca, default-router-local-ca, nats-site-ca, and default-nats-local-ca itself, self-signed, valid for 60 months, the first time that node is reconciled, unless you supplied those CAs on deploy. See Securing a remote control plane. These are separate from the Certificates catalog.
| Change | Where it is written | How the node picks it up |
|---|---|---|
| Router listeners, connectors, TLS profiles | config on the router system microservice | Edgelet change list |
| Service bridge on the default router | Same microservice config (bridges.tcpListeners / tcpConnectors) | Same change list. See Services. |
NATS server.conf or leaf.conf | ConfigMap nats-server-conf-{agent}, key server.conf, mounted at /etc/nats/config | Volume mount on the nats system microservice |
| Account JWT bundle on the hub | ConfigMap iofog-nats-jwt-bundle, mounted at /tmp/nats/jwt | Same volume-mount path |
| Cluster membership | Every server's server.conf is rendered again. Growing from one server to many rebuilds the local NATS container. | Change list, and rebuild when the template changes |
| Host or role change | Certificates reissued, Secrets remounted | Change list, volumeMounts |
The Controller owns these objects. There is no operator rewrite to preserve. Image and port behavior for the containers is on Router and NATS Server.
potctl get system-microservices -n my-ecn
potctl describe system-microservice system-<controller-agent>/router -n my-ecn
potctl describe system-microservice system-<controller-agent>/nats -n my-ecn
Day-2
The console does not install this control plane. After deploy, open Overview. Cluster controllers shows Active, Standby, or Stale. That block does not open a detail panel.
Connect
Connect stores the file in an empty namespace when you did not deploy from this workstation. For a later Controller add-on, connect with the full YAML, including auth and controller.
potctl connect -f remote-controlplane.yaml -n prod
potctl describe controlplane
Fix SSH details with configure when remote exec or logs fail.
Describe
potctl describe controlplane -n prod
potctl describe controller ctrl-1 -n prod
Upgrade
Set spec.controller.package.image, spec.systemMicroservices, and the system Edgelet pin, then deploy the same file. The full order is Upgrade the platform train. Pins are in Default image pins.
potctl deploy -f remote-controlplane.yaml -n prod
Fleet Edgelet nodes use upgrade. See Upgrade and rollback Edgelet. When Router or NATS on a Controller host looks stale, reconcile that system Edgelet node. See Reconcile platform.