Autofission dynamically calculates and updates the maximum replica limit (MaxScale) for explicitly opted-in Fission Functions. It derives the limit from the Kubernetes cluster's current schedulable capacity.
A Fission Function using the newdeploy executor can scale down when demand disappears. Its Horizontal Pod Autoscaler (HPA) still needs a fixed positive MaxScale. A limit sized for today's cluster becomes too low when nodes are added. An arbitrarily high limit can flood the scheduler with Pods that cannot fit.
Autofission keeps the limit current by measuring schedulable capacity on each node. It subtracts the requests of existing workloads and accounts for the Function's current replicas. It is designed for elastic, bare-metal, homelab, and edge clusters where nodes come and go and idle compute should remain available to Functions without displacing ordinary services.
Autofission sets only this upper bound. Fission's HPA and idle reaper still decide when the replica count grows and shrinks.
flowchart TD
state["Cluster state<br/>Nodes, Pods, and resource requests"] --> autofission["Autofission<br/>calculates safe capacity"]
autofission -->|updates| limit["Function MaxScale"]
demand["Demand or idle time"] --> scaling["Fission HPA<br/>and idle reaper"]
limit -->|sets upper bound| scaling
scaling -->|changes| replicas["Function replicas"]
Table of Contents
- Installation
- Quick start
- How it works
- Configuration
- RBAC and security
- Operations
- Compatibility and limitations
- Troubleshooting
- Development and releases
Installation
Use the Python CLI for optional local or one-shot runs. Install the Helm chart to run Autofission continuously in a cluster.
Python CLI
For local use, Autofission requires Python 3.8 or newer and is tested through Python 3.15:
python -m pip install autofission
autofission --help
Outside a Pod, the CLI uses the current kubeconfig context. Inside Kubernetes it uses the mounted ServiceAccount credentials.
To run one reconciliation without starting a daemon:
autofission --once --context my-cluster
This performs a real reconciliation and can update opted-in Functions.
Install in a cluster
Prerequisites are Kubernetes and an existing Fission installation. Autofission manages only Functions that use the newdeploy executor. The chart installs the controller, its RBAC, and two PriorityClasses; it does not install or remove Fission.
Set AUTOFISSION_VERSION to the chart version you want to install, then install Autofission from its OCI release:
helm upgrade --install autofission \
oci://ghcr.io/pomponchik/charts/autofission \
--version "${AUTOFISSION_VERSION}" \
--namespace fission \
--create-namespace \
--atomic \
--wait
For an unreleased checkout, replace the OCI URL with ./deploy/helm/autofission. Autofission assumes fetcher requests of 10m CPU and 16Mi memory. If Fission uses different values, pass them to the chart so the capacity calculation remains accurate:
helm upgrade --install autofission ./deploy/helm/autofission \
--namespace fission \
--set controller.fetcherCpuRequest=20m \
--set controller.fetcherMemoryRequest=32Mi
After the chart creates its PriorityClasses, configure Fission to use the low, non-preempting runtime class. This gives ordinary workloads precedence over elastic Function Pods. With the default Autofission release name, add the following to Fission's Helm values and upgrade Fission before opting in any Functions:
runtimePodSpec:
enabled: true
podSpec:
priorityClassName: autofission-runtime
Quick start
The example uses a Function named hello in the fission-function namespace. Replace both values with your Function's name and namespace, then opt it in:
kubectl label function hello \
--namespace fission-function \
autoscaling.fission.io/cluster-capacity=true
Wait for one reconciliation interval (15 seconds by default), then inspect the Function's MaxScale, the recorded calculation, and the HPA:
kubectl get function hello --namespace fission-function \
-o jsonpath='{.spec.InvokeStrategy.ExecutionStrategy.MaxScale}{"\n"}'
kubectl get function hello --namespace fission-function \
-o jsonpath='{.metadata.annotations.autoscaling\.fission\.io/calculated-maxscale}{"\n"}'
kubectl get hpa --namespace fission-function
After a successful cycle, the first two values should match. The HPA may reflect the new limit slightly later because Fission updates it asynchronously.
Removing the label stops future management. Autofission deliberately does not guess or restore a previous manually configured MaxScale; the last calculated value and informational annotations remain until you change them.
How it works
Each cycle is fail-safe and idempotent:
- List opted-in Functions, along with Fission Environments, Kubernetes Nodes, and Pods.
- Keep Ready, uncordoned, non-deleting nodes. Nodes with
NoScheduleorNoExecutetaints are excluded by default. - Calculate effective CPU and memory requests for every active, scheduled Pod, including containers, init containers and sidecars, Pod-level requests, and overhead. Exclude completed and unbound Pods.
- Resolve each Function's CPU and memory requests, inheriting missing or zero values from its Environment, then add the fetcher request. Use larger values observed on an existing Function Pod.
- Add the Function's existing Pods back to its capacity budget because step 3 counted them as other workload. This changes only the calculation, not the Pods. Then calculate how many identical Pods fit on each node. Per-node results are summed, so CPU on one node cannot combine with memory on another.
- Set
MaxScaleto at leastMinScaleand 1, then patch only Functions whose value changed. AresourceVersionconflict prevents concurrent user edits from being overwritten.
An invalid or conflicting Function does not block the others, but the controller remains NotReady until every managed Function completes cleanly.
Configuration
CLI flags take precedence over non-empty environment variables, which take precedence over defaults.
| CLI flag | Environment variable | Helm value | Default |
|---|---|---|---|
--interval-seconds |
AUTOFISSION_INTERVAL_SECONDS |
controller.intervalSeconds |
15 |
--request-timeout-seconds |
AUTOFISSION_REQUEST_TIMEOUT_SECONDS |
controller.requestTimeoutSeconds |
10 |
--retry-attempts |
AUTOFISSION_RETRY_ATTEMPTS |
controller.retryAttempts |
3 |
--fetcher-cpu-request |
AUTOFISSION_FETCHER_CPU_REQUEST |
controller.fetcherCpuRequest |
10m |
--fetcher-memory-request |
AUTOFISSION_FETCHER_MEMORY_REQUEST |
controller.fetcherMemoryRequest |
16Mi |
--managed-label |
AUTOFISSION_MANAGED_LABEL |
controller.managedLabel |
autoscaling.fission.io/cluster-capacity |
--managed-value |
AUTOFISSION_MANAGED_VALUE |
controller.managedValue |
true |
--include-tainted-nodes |
AUTOFISSION_INCLUDE_TAINTED_NODES |
controller.includeTaintedNodes |
false |
--log-level |
AUTOFISSION_LOG_LEVEL |
controller.logLevel |
INFO |
--state-directory |
AUTOFISSION_STATE_DIRECTORY |
— | /tmp/autofission (CLI); /var/run/autofission (chart) |
--kubeconfig, --context, and --in-cluster select credentials. --once runs exactly one pass. --probe readiness|liveness --max-age-seconds N is intended for Kubernetes exec probes.
RBAC and security
The chart grants only these cluster-wide operations:
listNodes and Pods;listFission Environments;listandpatchFission Functions.
It cannot read Secrets, create or delete Functions, or mutate Pods, Nodes, Deployments, or Services. Kubernetes RBAC cannot restrict list or patch permissions by label, so the opt-in label is an application-level boundary. Listing Pods exposes their specifications, including literal environment-variable values, to the controller process. This access is needed to protect capacity requested by other workloads.
The container runs as UID/GID 65532 with a read-only root filesystem. It drops all Linux capabilities, blocks privilege escalation, and uses a RuntimeDefault seccomp profile. The chart denies ingress but leaves egress unrestricted because a portable policy cannot select every cluster's API endpoint without hard-coded addresses.
The controller has a high PriorityClass so it remains available while Function Pods fill the cluster. The autofission-runtime class is negative and uses preemptionPolicy: Never; configure Fission to use it as shown under installation. Accurate resource requests remain essential because Autofission budgets requests like the scheduler rather than measuring live CPU or memory usage.
Treat permission to set the opt-in label as permission to consume the cluster's elastic budget. In a multi-tenant cluster, restrict that label with your admission policy. Autofission does not implement cross-Function quotas or fair sharing.
Operations
The chart runs one replica with a Recreate strategy, preventing two writers from racing without requiring leader election. After a node or cluster restart, Kubernetes recreates the controller Pod and Autofission rebuilds its state from the API.
Useful checks:
kubectl rollout status deployment/autofission --namespace fission
kubectl auth can-i list pods \
--as=system:serviceaccount:fission:autofission --all-namespaces
kubectl auth can-i patch functions.fission.io \
--as=system:serviceaccount:fission:autofission --all-namespaces
kubectl auth can-i get secrets \
--as=system:serviceaccount:fission:autofission --all-namespaces
The rollout should complete; the Pod-list and Function-patch checks should say yes, and the Secret check should say no.
Before uninstalling, remove the opt-in labels and set each Function's MaxScale to the limit you want to retain: stopping Autofission does not restore older values.
Uninstalling removes the controller resources but retains the runtime PriorityClass because Fission may still reference it during future cold starts. Fission CRDs, Functions, Environments, and namespaces remain untouched. Remove the PriorityClass manually only after changing Fission's runtimePodSpec.
Compatibility and limitations
- Only Fission
newdeployFunctions are managed.poolmgr, empty executor, andcontainerare rejected as opt-in configuration errors. - Every managed Function receives the full capacity it could use by itself. This preserves burst capacity, but simultaneous cold bursts can temporarily create Pending Pods. Later cycles account for scheduled peers and reduce the limits; Autofission is not a fairness scheduler.
- Fission and Kubernetes require a positive HPA maximum, so capacity below one replica produces
MaxScale=1. An explicitMinScaleis honored even when it exceeds currently free capacity. - The Fission v1 API and Environment resource inheritance are supported and tested with Fission 1.27.0.
- Capacity includes CPU, memory, and Pod slots. It does not model storage, GPUs and other extended resources, quotas, topology or affinity, per-Function scheduling constraints, image architecture, or unscheduled third-party Pods.
- Tainted nodes are excluded unless
--include-tainted-nodesis explicitly set. Only enable it when Fission runtime Pods actually tolerate those taints. - Extra sidecars and runtime PodSpec overhead are learned from a running Function Pod. Before the first replica, the estimate consists of the resolved runtime container plus configured fetcher requests.
- Low, non-preempting priority prevents Function Pods from evicting existing workloads. It cannot prevent node-pressure eviction when requests are inaccurate or nodes run at their physical limit; reserve headroom and set accurate requests.
- Very large clusters should benchmark API-server load and controller memory before shortening the default interval.
- Cluster-scoped RBAC and PriorityClass names use the release name, so reusing a name in another namespace causes collisions. Install Autofission once per cluster unless you set unique
fullnameOverrideandpriorityClasses.*.namevalues.
Troubleshooting
Autofission is NotReady — inspect controller logs. A 403 indicates custom RBAC or a ServiceAccount mismatch. A 409 means the Function changed after it was listed and will be retried safely on the next cycle. Other errors name the Function where possible.
The calculated limit is smaller than expected — check cordons, Ready status, taints, Pod requests, Pod slots, fetcher values, and per-node fragmentation. Capacity cannot combine spare CPU and spare memory located on different nodes.
Function Pods remain Pending — verify the runtime PriorityClass, taints/tolerations, node selectors, architecture, quota, storage, and concurrent managed Functions. Those constraints can be stricter than Autofission's current capacity model.
The HPA has not changed yet — Autofission patches the Function CR. Fission's executor reconciles that change into the HPA asynchronously; controller readiness only confirms the Function patch cycle.
Development and releases
Set up a development environment and run the local checks:
python -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements_dev.txt -e .
ruff check autofission tests
ruff format --check autofission tests
mypy --strict autofission
mypy tests
coverage run -m pytest -m "not e2e"
coverage report --fail-under=100
python -m build
twine check --strict dist/*
helm lint deploy/helm/autofission
The end-to-end suite creates a disposable Kind cluster, installs Fission, Metrics Server, and the chart, then tests scale-out, scale-in, capacity contraction, and non-preemption. Docker, Kind, kubectl, Helm, and the Fission CLI are required:
AUTOFISSION_E2E=1 pytest -m e2e -vv
Set AUTOFISSION_E2E_KEEP_CLUSTER=1 when debugging to preserve the generated cluster. Set AUTOFISSION_E2E_ARTIFACTS to choose where failure diagnostics are written. Do not run multiple copies of this suite against the same cluster; every pytest session deliberately creates and owns a separate Kind cluster.
Pushes run lint, unit tests, and the end-to-end suite. A push to main publishes the current version to PyPI, a multi-architecture image to GHCR, and the Helm chart as an OCI artifact.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file autofission-0.0.2.tar.gz.
File metadata
- Download URL: autofission-0.0.2.tar.gz
- Upload date:
- Size: 64.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d49b12557dc877d01390cd5abfae0d321ba18b959e29467157d805aa69e2f38d
|
|
| MD5 |
a8bf27ed1a410f355c36ba74237666c6
|
|
| BLAKE2b-256 |
87eeff6643ee82f3a2c5fc7f30f6efa963e13e8c8261d7efbd4142baed4866c9
|
Provenance
The following attestation bundles were made for autofission-0.0.2.tar.gz:
Publisher:
release.yml on pomponchik/autofission
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
autofission-0.0.2.tar.gz -
Subject digest:
d49b12557dc877d01390cd5abfae0d321ba18b959e29467157d805aa69e2f38d - Sigstore transparency entry: 2540077052
- Sigstore integration time:
-
Permalink:
pomponchik/autofission@dbdce616a5626c919d8add7dbaa6d622d7a44002 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/pomponchik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@dbdce616a5626c919d8add7dbaa6d622d7a44002 -
Trigger Event:
push
-
Statement type:
File details
Details for the file autofission-0.0.2-py3-none-any.whl.
File metadata
- Download URL: autofission-0.0.2-py3-none-any.whl
- Upload date:
- Size: 25.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
394b13b71de83bcfcdd2db5ca0969b1db282df148b0ada9d8927c5d2a139ae7e
|
|
| MD5 |
505aa6e3730be26a46053924cb978590
|
|
| BLAKE2b-256 |
75443021e273d1ff702906ed8487e4c446e65904d66a1b9a9b7732c8bda1335f
|
Provenance
The following attestation bundles were made for autofission-0.0.2-py3-none-any.whl:
Publisher:
release.yml on pomponchik/autofission
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
autofission-0.0.2-py3-none-any.whl -
Subject digest:
394b13b71de83bcfcdd2db5ca0969b1db282df148b0ada9d8927c5d2a139ae7e - Sigstore transparency entry: 2540077458
- Sigstore integration time:
-
Permalink:
pomponchik/autofission@dbdce616a5626c919d8add7dbaa6d622d7a44002 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/pomponchik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@dbdce616a5626c919d8add7dbaa6d622d7a44002 -
Trigger Event:
push
-
Statement type: