Server Configuration
Nstance Server reads a JSONC configuration file from object storage at shard/{shard}/config.jsonc.
The top-level shape is:
cluster- cluster-scoped identity, storage, secrets, and cluster leader election settings.shard- shard-scoped server runtime, infrastructure provider, subnet, and shard leader election settings.load_balancers- load balancers that groups can register instances with.images- optional image lookup configuration.certificates- certificate templates for files generated by the server.defaults- global args, vars, and userdata inherited by templates/groups.templates- instance templates referenced by groups and on-demand instances.groups- tenant-scoped static group configuration.
Below is an example/reference configuration file for Nstance Server, using example data for a fictional AWS-backed Kubernetes cluster:
{
"cluster": {
"id": "example-cluster", // Cluster ID (lowercase alphanumeric + hyphens)
// Optional: separate storage for cluster-scoped data. If omitted, the shard bucket is used with the "cluster/" prefix.
"storage": {
"provider": "s3",
"bucket": "example-nstance-state",
"region": "us-west-2",
"prefix": "cluster/"
},
"secrets": {
"provider": "object-storage",
"prefix": "secret/",
"encryption_key": {
"provider": "aws-secrets-manager",
"source": "arn:aws:secretsmanager:us-west-2:123412341234:secret:nstance/example-cluster/key"
},
// Optional: old encryption keys are decryption-only during key rotation.
"old_encryption_keys": [
{
"provider": "aws-secrets-manager",
"source": "arn:aws:secretsmanager:us-west-2:123412341234:secret:nstance/example-cluster/old-key"
}
],
// Optional: secret cache TTL. Omit or set to 0 to disable caching.
"cache_ttl": "5m"
},
// Optional: cluster-wide leader election timings. Omit to use server defaults.
"leader_election": {
"enabled": true,
"frequent_interval": "5s",
"infrequent_interval": "30s",
"leader_timeout": "15s"
}
},
"shard": {
"id": "us-west-2a", // Shard ID (lowercase alphanumeric + hyphens)
"infra": {
"provider": "aws",
"region": "us-west-2",
"zone": "us-west-2a", // aka CAPI "Failure Domain"
"options": {
"profile": "production"
}
},
"bind": {
"health_addr": "0.0.0.0:8990", // HTTP health endpoint for ASG/LB health checks (503 until config is loaded, then 200)
"election_addr": "0.0.0.0:8991", // HTTPS leader election health endpoint for peer-to-peer leader election health checks
"registration_addr": "0.0.0.0:8992", // Agent/Operator registration (anonymous)
"operator_addr": "0.0.0.0:8993", // Operator connections (authenticated)
"agent_addr": "0.0.0.0:8994" // Agent connections (authenticated)
},
"advertise": {
"health_addr": "10.0.0.1:8990", // HTTP health endpoint advertised for ASG/LB health checks and monitoring
"election_addr": "10.0.0.1:8991", // HTTPS leader election health endpoint advertised to peer servers
"registration_addr": "10.0.0.1:8992", // Address used by Nstance Agents and Nstance Operator to register
"operator_addr": "10.0.0.1:8993", // Address used by Nstance Operator to connect
"agent_addr": "10.0.0.1:8994" // Address used by Nstance Agents to connect
// If the host part is empty, "0.0.0.0", or "::", it is auto-detected from the primary network interface.
},
// Optional: stable leader network configuration for shard leadership.
"leader_network": {
// AWS requires both ip and interface_id (ENI ID). GCP uses ip and leaves interface_id empty.
"ip": "10.0.0.100", // Stable leader IP address (ENI private IP for AWS, reserved IP for GCP)
"interface_id": "eni-0abc123def456789" // AWS ENI ID (required for AWS, not used for GCP)
},
"subnet_pools": {
"control-plane": ["subnet-12345678"], // Maps subnet pools to provider subnet IDs
"ingress": ["subnet-23456789"], // Subnets for ingress/load balancer nodes
"workers": ["subnet-87654321", "subnet-abcdef"] // Each key can map to multiple subnets for capacity
},
// Optional: restrict dynamic groups to these subnet pool IDs. Omit or set empty to allow any configured subnet pool.
"dynamic_subnet_pools": ["workers"],
// Optional: omit shard timing fields to use server defaults.
"request_timeout": "30s",
"create_rate_limit": "1s",
"health_check_interval": "60s",
"default_drain_timeout": "5m",
"image_refresh_interval": "6h", // How often to refresh image resolutions
"shutdown_timeout": "10s",
// Optional: omit garbage_collection to use server defaults.
"garbage_collection": {
"interval": "2m", // How often to backfill provider instances and look for dangling ones
"registration_timeout": "5m", // How long to wait for instance registration before terminating as dangling
"deleted_record_retention": "30m" // How long to keep deleted instance records in the local database
},
// Optional: omit expiry to disable age-based expiry.
"expiry": {
"eligible_age": "504h", // 21 days
"forced_age": "720h", // 30 days
"ondemand_age": "168h" // 7 days
},
// Optional: shard leader election timings. Omit to use server defaults.
"leader_election": {
"enabled": true,
"frequent_interval": "5s",
"infrequent_interval": "30s",
"leader_timeout": "15s"
},
// Optional: jittered delay range before exiting after fatal errors. Omit to use server defaults.
"error_exit_jitter": {
"min_delay": "10s",
"max_delay": "40s"
}
},
// Optional: logical backend registration targets that groups can reference by key.
// AWS targets use target group ARNs; GCP targets use unmanaged instance groups.
"load_balancers": {
"www": {
"provider": "aws",
"target_group_arns": [
"arn:aws:elasticloadbalancing:us-west-2:123412341234:targetgroup/example-www/abcdef1234567890"
]
}
},
// Optional: image lookup configuration. Omit if templates use fixed provider image IDs directly.
"images": {
"debian_13_arm64": {
"provider": "aws",
"filters": [
{"name": "name", "values": ["debian-13-arm64-*"]},
{"name": "virtualization-type", "values": ["hvm"]},
{"name": "architecture", "values": ["arm64"]}
],
"owners": ["136693071363"], // Debian
"sort": "creation-date",
"order": "desc",
// Optional: fallback image ID if lookup fails and no cached image is available.
"fallback": "ami-026fccd88446aa0bf"
},
"debian_13_amd64": {
"provider": "aws",
"filters": [
{"name": "name", "values": ["debian-13-amd64-*"]},
{"name": "virtualization-type", "values": ["hvm"]},
{"name": "architecture", "values": ["x86_64"]}
],
"owners": ["136693071363"],
"sort": "creation-date",
"order": "desc",
// Optional: fallback image ID if lookup fails and no cached image is available.
"fallback": "ami-0abcd1234efgh5678"
}
},
// Optional: certificate templates for generated files.
"certificates": {
"kubelet.client": {
"kind": "client",
"cn": "system:node:{{ .Instance.ID }}",
"organization": ["system:nodes"],
"dns": [
"localhost",
"{{ .Instance.ID }}",
"{{ .Instance.Hostname }}"
],
"ip": [
"127.0.0.1",
"::1",
"{{ .Instance.IP4 }}",
"{{ .Instance.IP6 }}"
],
// Optional: certificate TTL in hours. Omit to use server default.
"ttl": 8760
},
"kubelet.server": {
"kind": "server",
"cn": "kubelet",
"dns": [
"localhost",
"{{ .Instance.ID }}",
"{{ .Instance.Hostname }}"
],
"ip": [
"127.0.0.1",
"::1",
"{{ .Instance.IP4 }}",
"{{ .Instance.IP6 }}"
]
},
"kube-apiserver.server": {
"kind": "server",
"cn": "kubernetes",
"dns": [
"localhost",
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local",
"kubernetes.svc.cluster.local",
"{{ .Instance.ID }}",
"{{ .Instance.Hostname }}",
"{{ .Vars.ClusterFQDN }}"
],
"ip": [
"127.0.0.1",
"::1",
"198.18.0.1",
"fdc6::1",
"{{ .Instance.IP4 }}",
"{{ .Instance.IP6 }}"
]
}
},
"defaults": {
"args": {
// Passed to the provider instance creation API. For AWS, this maps to RunInstances.
// Nstance manages instance type, subnet, userdata, and count fields.
"Ipv6AddressCount": 1,
"BlockDeviceMappings": [
{
"DeviceName": "/dev/sda1", // root volume
"Ebs": {
"VolumeSize": 50, // in GB
"VolumeType": "gp3",
"Encrypted": true
}
}
],
"MetadataOptions": {
"HttpEndpoint": "enabled", // default
"HttpProtocolIpv6": "disabled", // default
"HttpPutResponseHopLimit": 1, // default
"HttpTokens": "required", // always require IMDSv2
"InstanceMetadataTags": "enabled"
},
"PrivateDnsNameOptions": {
"EnableResourceNameDnsARecord": true
},
"TagSpecifications": [
{
"ResourceType": "instance",
"Tags": [
{"Key": "InstanceId", "Value": "{{ .Instance.ID }}"},
{"Key": "InstanceKind", "Value": "{{ .Instance.Kind }}"}
]
}
]
},
"vars": {
"ClusterSlug": "example-cluster",
"ClusterFQDN": "example-cluster.cluster.cool",
"Environment": "production",
"CloudBillingID": "123412341234",
"SSHAuthorizedKey": "",
"TelemetryBucket": ""
},
// Optional: global userdata used by templates that do not define userdata.
"userdata": {
"content": "#!/bin/bash\necho \"default userdata for {{ .Instance.ID }}\""
}
},
"templates": {
"nat": {
"kind": "nat",
"arch": "arm64",
"args": {
"ImageId": "{{ .Image.debian_13_arm64 }}", // resolved from images config
"SecurityGroupIds": ["sg-12341234abcd1234ab"],
"IamInstanceProfile": {
"Arn": "arn:aws:iam::123412341234:role/example-instance-role"
}
},
"userdata": {
"content": "#!/bin/bash\necho \"hello from {{ .Instance.ID }}\""
},
// template defaults:
"size": 1,
"instance_type": "t4g.small",
"subnet_pool": "control-plane",
"vars": {}
},
"knc": {
"kind": "knc", // 3 lowercase letters, used as prefix for instance ID (puidv7)
"arch": "arm64", // used to validate instance type
"files": {
// send secret `tunnel-20250924.json` as `tunnel.json` file
"tunnel.json": {
"kind": "secret",
"source": "tunnel-20250924.json"
},
// send a file from object storage
"bootstrap-config.json": {
"kind": "storage",
"source": "files/{{ .Vars.Environment }}/bootstrap-config.json"
},
// send a certificate generated using a public key sent from the agent
"kubelet.client.crt": {
"kind": "certificate",
"template": "kubelet.client",
"key": {
"source": "agent",
"name": "kubelet.client.pub"
}
},
// send templated environment file
"instance.env": {
"kind": "env",
"template": {
"INSTANCE_ID": "{{ .Instance.ID }}",
"ENVIRONMENT": "{{ .Vars.Environment }}",
"K8S_NODE_LABELS": "{{ .Vars.KUBELET_NODE_LABELS }}",
"CLUSTER_FQDN": "{{ .Vars.ClusterFQDN }}"
}
},
// send templated JSON configuration file
"kubelet-config.json": {
"kind": "json",
"template": {
"kind": "KubeletConfiguration",
"apiVersion": "kubelet.config.k8s.io/v1beta1",
"address": "{{ .Instance.IP4 }}",
"port": 10250,
"cgroupDriver": "systemd",
"clusterDomain": "cluster.local",
"nodeLabels": {
"instance.example.com/id": "{{ .Instance.ID }}"
}
}
},
"custom.conf": {
"kind": "string",
"template": "instance={{ .Instance.ID }}\ncluster={{ .Vars.ClusterSlug }}\n"
}
},
"args": {
"ImageId": "{{ .Image.debian_13_arm64 }}", // resolved from images config
"SecurityGroupIds": ["sg-23412341abcd1234ab"],
"IamInstanceProfile": {
"Arn": "arn:aws:iam::123412341234:role/example-instance-role"
},
// Example of overriding a default arg. Objects are merged recursively; non-object values replace the lower-priority value.
"BlockDeviceMappings": [
{
"Ebs": {
"VolumeSize": 100 // in GB
}
}
]
},
"userdata": {
"source": "url",
"content": "https://example.com/userdata/knc_arm64.sh"
},
// template defaults:
"size": 1,
"instance_type": "t4g.medium",
"subnet_pool": "control-plane",
"vars": {
"K8SAPIHostname": "example-cluster.cluster.cool",
"OIDCIssuer": "https://auth.example.com",
"RegistryBucket": ""
}
},
"knd": {
"kind": "knd",
"arch": "arm64",
"args": {
"ImageId": "{{ .Image.debian_13_arm64 }}"
},
"instance_type": "t4g.large",
"subnet_pool": "workers"
}
},
"groups": {
"default": {
"nat": {
"template": "nat",
"size": 1,
"subnet_pool": "control-plane" // Subnet pool ID (resolved via shard.subnet_pools)
},
"main": {
"template": "knc",
"size": 1,
"subnet_pool": "control-plane",
"vars": {
"KUBELET_NODE_LABELS": "controlplane"
}
},
"ingress": {
"template": "knd",
"subnet_pool": "ingress",
"load_balancers": ["www"],
"vars": {
"KUBELET_NODE_LABELS": "traefik"
}
},
"apps": {
"template": "knd",
"size": 2,
"instance_type": "t4g.xlarge",
"subnet_pool": "workers",
"drain_timeout": "10m",
"vars": {}
}
}
}
}cluster
The cluster block defines cluster-scoped configuration shared by all shards:
id- cluster identifier.storage- optional separate cluster-scoped storage. If omitted, the shard bucket is used with thecluster/prefix.secrets- secret store configuration and encryption key settings.leader_election- optional cluster leader election timing settings.
shard
The shard block defines the Nstance Server process and shard-local infrastructure behavior:
id- unique shard identifier.infra- infrastructure provider (aws,gcp,mock,tmux, orproxmox), region, zone, and provider-specific options.bind- addresses the server listens on.advertise- addresses clients should connect to.leader_network- stable shard leader IP configuration.subnet_pools- maps subnet pool IDs to provider subnet IDs.dynamic_subnet_pools- optional list limiting which subnet pools dynamic groups can use.request_timeout- provider request timeout.create_rate_limit- minimum delay between provider create calls.health_check_interval- agent health check interval.default_drain_timeout- default graceful drain timeout for group scale-down/replacement.image_refresh_interval- image lookup refresh interval.shutdown_timeout- graceful server shutdown timeout.garbage_collection- provider backfill and dangling instance cleanup settings.expiry- optional instance age limits for automatic rotation.leader_election- optional shard leader election timing settings.error_exit_jitter- optional delay range before exiting after fatal startup/runtime errors.
load_balancers
Load balancers are defined at the top level and referenced by group load_balancers lists. Each entry is a logical backend registration target, not necessarily a distinct physical load balancer. For example, separate ingress and controlplane entries may point at different listener backends on the same provider load balancer, or at different load balancers.
AWS load balancers use:
{
"provider": "aws",
"target_group_arns": ["arn:aws:elasticloadbalancing:..."]
}GCP load balancers use:
{
"provider": "gcp",
"instance_group_name": "example-instance-group"
}For GCP, Nstance adds and removes instances from the unmanaged instance group. Infrastructure tooling such as Terraform should create the instance group, create the load balancer, and configure its backend service(s) to use that instance group.
certificates and template files
certificates defines reusable TLS certificate templates. Templates can be referenced by template files entries with kind: "certificate".
Template files support these kinds:
certificate- generate a certificate from a named certificate template. Thekeymust explicitly name a public key sent from the agent.secret- fetch a secret bysourcefrom the configured secrets store.storage- fetch an object bysourcefrom object storage. Thesourcesupports Go template variables.env- render a.envfile from a string-valued template object.json- render a JSON file from a template object.string- render a raw string template for custom formats.
defaults, templates, and groups
defaults, template config, and group config are merged to produce the final instance configuration.
defaults.args,templates.<template>.args, andgroups.<tenant>.<group>.argsare merged with later layers overriding earlier layers.defaults.vars,templates.<template>.vars, andgroups.<tenant>.<group>.varsare merged the same way.defaults.userdatais used when a template does not specify userdata.templatesdefine reusable instance configuration.groupsare nested by tenant:groups.<tenant>.<group>.
For more information see: Image Resolution, Args & Vars Merge Strategy, Args, Vars, and Userdata Templates.