Installing an HTTP server#
In airgapped environments, child cluster nodes download the k0s binary from a URL configured in
the ClusterTemplate. That URL propagates into k0sControllerConfig bootstrap data and is used
by CAPI controllers during node bootstrapping. The URL must point to a stable, statically
addressable endpoint.
Note
This guide describes how to deploy a dedicated HTTP file server on the management cluster when
no suitable file server is already available in your environment. Production environments
commonly have existing infrastructure for this purpose — an internal Artifactory instance, an
S3-compatible object store, an nginx or Apache server, or a corporate file share exposed over
HTTP/HTTPS. Check with your infrastructure or platform team before following this guide. If a
suitable file server is already reachable from your child cluster node networks, you only need to
place the k0s binary in the appropriate directory and configure its URL during install by setting
the Mirantis k0rdent Enterprise helm chart value controller.globalK0sURL.
Prerequisites#
Before proceeding, ensure the following:
- A running Mirantis k0rdent Enterprise management cluster.
- The
registry.local/nginx:1.30.2container image is available to the management cluster. - A
StorageClassis available in the management cluster. Runkubectl get storageclassto verify. - A load balancer controller that provisions
Serviceresources of typeLoadBalancer(e.g., MetalLB, or the load balancer provider for your infrastructure platform).
Warning
The nginx:1.30.2 container image is not included in the Mirantis k0rdent Enterprise
airgap bundle. Push it to your local image registry as registry.local/nginx:1.30.2 (replacing
registry.local with your actual registry hostname) before deploying the file server.
Operational considerations#
- Firewall rules: Open port 80 from child cluster node network ranges to the load balancer IP.
- The file server uses a
PersistentVolumeClaimfor binary storage. The binary survives pod restarts, rescheduling, and management cluster restarts as long as the underlyingPersistentVolumeis intact. - When upgrading
k0s, add the new binary to the existing binary storage without overwriting the old one — clusters currently being provisioned may still be downloading it. See Updating k0s binaries on the file server. - For HTTPS file server endpoints with a custom CA certificate, also set
controller.k0sURLCertSecretto the name of a Secret containingca.crt. See Configuring a global k0s URL for details.
Preparing storage for the k0s file server#
Two storage options are available:
- PVC (recommended): Use a
PersistentVolumeClaimbacked by aStorageClass. The binary survives pod restarts, rescheduling, and management cluster restarts as long as the underlyingPersistentVolumeis intact. Requires aStorageClassin the management cluster. - hostPath (lab/single-node only): Place the binary directly on the node filesystem. Simple to set up, but not resilient — the binary is lost if the pod reschedules to a different node.
PVC (recommended)#
Save the following as k0s-fileserver-pvc.yaml. Replace your-storage-class with your
StorageClass name (run kubectl get storageclass to list available options) and adjust
storage size as needed:
---
apiVersion: v1
kind: Namespace
metadata:
name: k0s-fileserver
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: k0s-fileserver-data
namespace: k0s-fileserver
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: <your-storage-class> # Replace with your StorageClass name (kubectl get storageclass)
Apply:
kubectl apply -f k0s-fileserver-pvc.yaml
Verify the PVC is bound before proceeding:
kubectl get pvc k0s-fileserver-data -n k0s-fileserver
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
k0s-fileserver-data Bound pvc-a1b2c3d4-... 10Gi RWO standard 30s
Single-node or lab environments#
Warning
This approach is not suitable for production. A hostPath volume is tied to a specific node.
If the pod reschedules or the node is reprovisioned, the binary is lost and all subsequent child
cluster deployments will fail.
If you are running a single-node management cluster in a lab or development environment without a
StorageClass, skip the PVC steps above and instead place the binary directly on the node:
sudo mkdir -p /var/lib/k0s-fileserver/k0rdent-enterprise
sudo cp k0s-v1.35.1+k0s.1-amd64 /var/lib/k0s-fileserver/k0rdent-enterprise/
Then replace the volumes.files entry in the fileserver deployment manifest with:
- name: files
hostPath:
path: /var/lib/k0s-fileserver
type: DirectoryOrCreate
Load the k0s binary#
Note
The busybox:1.36.1 image is included in the Mirantis k0rdent Enterprise airgap bundle.
Use the image reference with your local registry prefix, for example
registry.local/k0rdent-enterprise/busybox:1.36.1. Replace registry.local with your actual
registry hostname.
Save the following as k0s-binary-loader.yaml:
---
apiVersion: v1
kind: Pod
metadata:
name: binary-loader
namespace: k0s-fileserver
spec:
restartPolicy: Never
containers:
- name: loader
image: registry.local/k0rdent-enterprise/busybox:1.36.1 # Replace registry.local with your registry hostname
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
- name: files
mountPath: /data
volumes:
- name: files
persistentVolumeClaim:
claimName: k0s-fileserver-data
Apply and wait for the pod to be ready:
kubectl apply -f k0s-binary-loader.yaml
kubectl wait --for=condition=Ready pod/binary-loader -n k0s-fileserver --timeout=60s
Note
Use the k0s binary downloaded in the Download k0s binaries step.
Copy the binary into the PVC:
kubectl exec -n k0s-fileserver binary-loader -- mkdir -p /data/k0rdent-enterprise
kubectl cp k0s-v1.35.1+k0s.1-amd64 k0s-fileserver/binary-loader:/data/k0rdent-enterprise/k0s-v1.35.1+k0s.1-amd64
Remove the loader pod when done:
kubectl delete pod binary-loader -n k0s-fileserver
Note
When upgrading k0s, add the new binary to the file server without removing the existing one.
See Updating k0s binaries on the file server
in the upgrade guide.
Deploying the file server#
A LoadBalancer Service receives a stable external IP address from your load balancer controller.
The port is statically declared and not subject to Kubernetes ephemeral port allocation.
Save the following as k0s-fileserver.yaml:
---
apiVersion: v1
kind: ConfigMap
metadata:
name: k0s-fileserver-config
namespace: k0s-fileserver
data:
default.conf: |
server {
listen 80;
server_name _;
root /var/www/html;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 70;
client_max_body_size 512m;
proxy_max_temp_file_size 0;
location / {
autoindex on;
}
location /healthz {
return 200 'OK';
add_header Content-Type text/plain;
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: k0s-fileserver
namespace: k0s-fileserver
labels:
app: k0s-fileserver
spec:
replicas: 1
selector:
matchLabels:
app: k0s-fileserver
template:
metadata:
labels:
app: k0s-fileserver
spec:
containers:
- name: nginx
image: registry.local/nginx:1.30.2 # Replace registry.local with your registry hostname; not in the airgap bundle
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
- name: files
mountPath: /var/www/html
volumes:
- name: config
configMap:
name: k0s-fileserver-config
- name: files
persistentVolumeClaim:
claimName: k0s-fileserver-data
---
apiVersion: v1
kind: Service
metadata:
name: k0s-fileserver
namespace: k0s-fileserver
spec:
type: LoadBalancer
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: k0s-fileserver
Apply:
kubectl apply -f k0s-fileserver.yaml
Wait for the external IP to be assigned:
kubectl get service k0s-fileserver -n k0s-fileserver --watch
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
k0s-fileserver LoadBalancer 10.96.12.34 192.168.1.100 80/TCP 90s
Note
EXTERNAL-IP will show <pending> until the load balancer controller assigns an address.
On bare-metal clusters using MetalLB, this requires a configured IPAddressPool.
Verify the endpoint is reachable:
export EXTERNAL_IP="$(kubectl get svc k0s-fileserver -n k0s-fileserver -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
export FILESERVER_URL="http://$EXTERNAL_IP/k0rdent-enterprise"
curl -I "$FILESERVER_URL/k0s-v1.35.1+k0s.1-amd64"
HTTP/1.1 200 OK
Server: nginx/1.30.2
Date: Tue, 17 Jun 2025 21:32:28 GMT
Content-Type: application/octet-stream
Content-Length: 268006461
Last-Modified: Tue, 17 Jun 2025 21:26:59 GMT
Connection: keep-alive
ETag: "6851dda3-ff9743d"
Accept-Ranges: bytes
Note
Recommended for long-term stability, create a DNS record pointing a hostname
(e.g. fileserver.k0rdent.local) to the load balancer address and use that hostname in
controller.globalK0sURL. Load balancer IPs can change over the lifetime of the cluster —
after a MetalLB reconfiguration or infrastructure reprovisioning. With a DNS record, only the
DNS entry needs updating; the Mirantis k0rdent Enterprise configuration and existing
ClusterDeployment objects remain unchanged.
Note
The file server can also be exposed over HTTPS (using a LoadBalancer Service with TLS termination) or via an Ingress resource, depending on what is available in your environment. These options require additional configuration specific to your infrastructure — such as certificate management, ingress controller setup, and DNS — and are not covered in this guide.
Using the download URL in a ClusterTemplate#
controller.globalK0sURL tells Mirantis k0rdent Enterprise where to download the k0s
binary for child cluster nodes. It is set as part of the airgapped installation — see
Airgapped installation for the full configuration reference. Instead of
pointing to the public Mirantis download server, set it to your file server base URL:
--set controller.globalK0sURL=http://<FILESERVER-ADDRESS>/k0rdent-enterprise
The value can also be updated after installation by editing the Management object under
.spec.core.kcm.config.
For HTTPS with a custom CA certificate, also set controller.k0sURLCertSecret to the name of a
Secret containing ca.crt. See
Configuring a global k0s URL
for details.
Warning
controller.globalK0sURL is a global setting — it applies to all child clusters deployed from
this management cluster. If the file server endpoint changes, you must update this value and
re-provision affected clusters. Treat the file server endpoint with the same stability guarantees
as a container registry address.