Native local development mode
slimfaas local starts functions, Jobs, auxiliary development processes, and
one to three real SlimFaas/Raft nodes directly as operating-system processes.
It is intended for development: processes share the host network and no CPU,
memory, or security isolation is applied.
Docker and Kubernetes are not required. Install the runtimes used by the commands in your manifest, such as the .NET SDK or Node.js, and ensure the configured port ranges are available.
The existing Local orchestrator remains available for deterministic tests.
The CLI uses the separate internal Process orchestrator and a single
loopback-only, token-authenticated supervisor.
Quick start
The repository contains a complete slimfaas.local.yaml
demo.
Run the demo from a Git clone
With the .NET 10 SDK, Node.js, and npm installed, clone the repository and start the demo directly from its root:
git clone https://github.com/SlimPlanet/SlimFaas.git
cd SlimFaas
dotnet run --project src/SlimFaas -- \
local validate -f ../../slimfaas.local.yaml
dotnet run --project src/SlimFaas -- \
local up -f ../../slimfaas.local.yaml
In Windows PowerShell, keep each command on one line because \ is not a
PowerShell line-continuation character:
dotnet run --project src/SlimFaas -- local validate -f ../../slimfaas.local.yaml
dotnet run --project src/SlimFaas -- local up -f ../../slimfaas.local.yaml
The first dotnet run restores and builds SlimFaas and its web interface. The
../../slimfaas.local.yaml path is relative to the src/SlimFaas working
directory configured by the repository launch profile.
Keep local up running and, from another terminal in the cloned repository,
test the dashboard and a demo function:
curl http://127.0.0.1:30020/status-functions
curl http://127.0.0.1:30020/function/fibonacci1/hello/local
Open http://127.0.0.1:30020/ to view the live interface. Press Ctrl+C in
the first terminal to stop the demo.
Run with an installed SlimFaas executable
From a directory containing the manifest, run:
slimfaas local validate
slimfaas local up
When the cluster is ready, the console prints:
Entrypoint: http://127.0.0.1:30020
Open that URL for the SlimFaas user interface, or use it as the base URL for
function, Job, event, and data requests. 30020 is the stable entrypoint in
the repository demo. Ports starting at 30021 are direct node ports and
should not be used as the application entrypoint in native local mode.
For example:
curl http://127.0.0.1:30020/status-functions
curl http://127.0.0.1:30020/function/fibonacci1/hello/local
Manifests and overlays
Select another manifest with -f or --file. Repeat the option to apply
partial overlays from left to right:
slimfaas local validate \
-f slimfaas.local.yaml \
-f slimfaas.local.dev.yaml \
--env-file .env.local
slimfaas local up \
-f slimfaas.local.yaml \
-f slimfaas.local.dev.yaml \
--env-file .env.local
When no -f is present, only slimfaas.local.yaml is loaded. SlimFaas does
not discover an override or .env file automatically.
When using the framework-dependent build from this repository:
dotnet run --project src/SlimFaas -- local validate \
-f ../../slimfaas.local.yaml
dotnet run --project src/SlimFaas -- local up \
-f ../../slimfaas.local.yaml
The repository launch profile uses src/SlimFaas as its working directory,
which is why repository-root files use the ../../ prefix in these commands.
Local secrets
YAML mappings are merged recursively, while scalars and sequences such as
command, dependsOn, and schedules are replaced by the last file that
defines them. An empty sequence clears an inherited sequence. Set a value to
null to remove an inherited field, annotation, environment variable,
function, Job, or auxiliary process:
functions:
fibonacci:
environment:
UNUSED_IN_DEV: null
command: ["dotnet", "watch", "run", "--project", "src/Fibonacci"]
Only the effective merged document must be complete. Runtime paths,
workingDirectory, and state.directory are always relative to the first
YAML file.
--env-file is repeatable and accepts dotenv entries (KEY=VALUE), comments,
quoted values, and an optional export prefix. Physical multiline values are
not supported. Later env-files override earlier ones, and the environment of
the slimfaas process has the highest priority. Values are only used when the
effective YAML references them:
functions:
fibonacci:
environment:
DATABASE_PASSWORD: "${FIBONACCI_DATABASE_PASSWORD}"
The merged YAML and env-file contents are not written to the state directory
or logs. Keep .env.local and secret overlays out of source control. Prefer
function, Job, or auxiliary-process environment entries for secrets: a
secret interpolated into command can be visible through operating-system
process inspection.
See the development overlay and the env-file example.
Use --clean to discard persistent local state:
slimfaas local up --clean
For safety, SlimFaas only deletes a directory containing its
.slimfaas-local.json ownership marker. Changing cluster.nodes for an
existing persistent state, or opening an unsupported marker schema, requires
--clean.
Kubernetes-compatible function metadata
Everything under functions.<name>.annotations uses the exact Kubernetes
annotation names and value formats. In particular, JSON strings for
SlimFaas/Configuration, SlimFaas/Schedule, and SlimFaas/Scale can be
copied from spec.template.metadata.annotations. SlimFaas/Function: "true"
is required.
Structural YAML keys are case-insensitive, so both environment and
Environment work. Documentation uses camel case.
SlimFaas/Scale.ReplicaMax limits scale-out. Without that annotation, the
maximum is max(ReplicasMin, ReplicasAtStart).
At local cluster startup, managed functions begin at ReplicasMin.
ReplicasAtStart remains the target used when an HTTP request, an event, or
/wake-function/{name} wakes a function from zero.
Route a function to an IDE debugger
Set functions.<name>.debugUrl in a local, unversioned overlay to route the
function to a process started by an IDE:
# slimfaas.local.debug.yaml
functions:
fibonacci1:
debugUrl: "http://127.0.0.1:5051"
Start the debug target with a fixed binding. For ASP.NET Core, set this in the IDE launch profile or environment:
ASPNETCORE_URLS=http://127.0.0.1:5051
Then apply the overlay after the normal manifest:
dotnet run --project src/SlimFaas -- local up \
-f ../../slimfaas.local.yaml \
-f ../../slimfaas.local.debug.yaml
Without the second -f, the function returns to its normal command. The
overlay is read only at startup; switching a target while the cluster is
running is not supported.
debugUrl must be an absolute http URL with an explicit port. A base path is
allowed, for example http://127.0.0.1:5051/my-function; query strings and
fragments are rejected. The port must not conflict with the entrypoint, node
HTTP, or Raft ports. A loopback or localhost debug port is also reserved from
processPorts and auxiliary processes.
While debug routing is enabled, SlimFaas does not start, restart, shut down, or
scale the function command. It advertises one virtual
<function-name>-debug replica and probes debugUrl + health.path. The
function is Starting until the IDE process answers successfully, becomes
Running, and returns to Starting if the process stops. Probes continue
without a fallback to the normal command. Synchronous calls keep their normal
HTTP timeout behavior, while asynchronous calls remain queued until the
endpoint is ready.
The URL can also be supplied from the existing .env.local mechanism:
functions:
fibonacci1:
debugUrl: "${FIBONACCI1_DEBUG_URL}"
FIBONACCI1_DEBUG_URL=http://127.0.0.1:5051
Pass --env-file ../../.env.local together with both manifests. The repository
ignores slimfaas.local.debug.yaml and .env.local so developer-specific
ports are not committed accidentally.
Dynamic ports
processPorts is one global pool shared by all functions and auxiliary
processes using port: auto. At scale-out, SlimFaas selects the first free
port, verifies it immediately before launch, and keeps it for the process or
replica identity across crash restarts. Function allocations are persisted in
persistent mode and released at scale-down.
The following placeholders are expanded independently for every replica in command arguments, environment values, annotations, health checks, and shutdown hooks:
{port}: the allocated TCP port;{replica}: the zero-based replica index.
SlimFaas always sets PORT, SLIMFAAS_PORT, and
SLIMFAAS_REPLICA_INDEX. If ASPNETCORE_URLS is absent, it defaults to
http://127.0.0.1:{port}. For an explicit ASP.NET Core binding:
environment:
ASPNETCORE_ENVIRONMENT: Development
ASPNETCORE_URLS: "http://127.0.0.1:{port}"
The real port is advertised in PodInformation.Ports and substituted into
annotations such as prometheus.io/port. If the pool is exhausted, the
desired replica remains visible with StartFailureReason=PortRangeExhausted
and is retried after a port becomes free.
Auxiliary development processes
Use processes to launch frontends, file watchers, emulators, and other tools
alongside the SlimFaas cluster:
processes:
fibonacci-front:
command: ["npm", "run", "dev", "--", "--host", "127.0.0.1", "--port", "{port}"]
workingDirectory: src/FibonacciReact
environment:
BROWSER: "none"
VITE_SLIMFAAS_URL: "http://127.0.0.1:30020"
port: auto
restartPolicy: always
Each entry starts exactly one process and is deliberately absent from
DeploymentInformation, autoscaling, function readiness, routing, and the
public SlimFaas control API. Its running state can only participate in the
local dependency checks described below. Commands run without a shell. On
Windows, executable wrappers such as npm.cmd and .bat files are resolved
through ComSpec; the same ["npm", "run", "dev"] command therefore remains
portable.
workingDirectory defaults to . and is relative to the first YAML file.
environment follows the same overlay, ${VARIABLE}, and secret-handling
rules as functions. Names must use lowercase letters, digits, ., _, or
-, start with a letter or digit, and contain at most 63 characters.
port can be omitted, set to a fixed integer, or set to auto. An automatic
port comes from processPorts; a fixed port may be inside or outside that
range. Configured process ports are reserved before functions start. When a
port is configured, {port} is replaced in command arguments and environment
values and PORT is injected. Using {port} without configuring port is a
validation error.
restartPolicy is case-insensitive and supports:
always(default): restart after any exit or launch failure;onFailure: restart only after a non-zero exit or launch failure;never: make one attempt, which is useful for one-off setup commands.
Retries use an exponential delay from 1 to 30 seconds, reset after 30 seconds
of stable execution. A process ending never terminates the cluster. During
shutdown, restarts are disabled and every remaining process tree is
terminated. Output is prefixed with [process/<name>] and also written to
logs/process-<name>.log.
Local-only process dependencies
A function, Job, or scheduled Job can wait for an auxiliary process by using
processes:<name> in its normal SlimFaas/DependsOn annotation:
functions:
orders-api:
annotations:
SlimFaas/Function: "true"
SlimFaas/DependsOn: "orders-database,processes:database-emulator"
processes:
database-emulator:
command: ["database-emulator", "--port", "{port}"]
port: auto
restartPolicy: always
In native local mode, processes:database-emulator is ready while that managed
operating-system process is running. A function waiting at zero is not scaled
up, and a Job remains queued, until all its process dependencies are running.
There is no health probe for auxiliary processes, and losing a dependency does
not scale down a function that is already running.
The referenced name must exist under processes; slimfaas local validate
rejects missing or empty process names. Docker and Kubernetes deliberately
ignore every processes: entry, so the same annotation can combine a deployed
dependency such as orders-database with its local development replacement.
For example, a one-off asset generator can be declared as:
processes:
generate-assets:
command: ["npm", "run", "generate"]
workingDirectory: src/FibonacciReact
restartPolicy: never
Jobs
Local Jobs use the same SlimFaas/* annotations as suspended Kubernetes
CronJobs. For example:
jobs:
fibonacci5:
command: ["dotnet", "run", "--project", "FibonacciBatch.csproj", "--"]
workingDirectory: src/FibonacciBatch
annotations:
SlimFaas/Job: "true"
SlimFaas/DefaultVisibility: "Public"
SlimFaas/NumberParallelJob: "1"
SlimFaas/DependsOn: "fibonacci1,fibonacci2"
SlimFaas/Schedules: '[{"Schedule":"*/2 * * * *","Args":["39"]}]'
ttlSecondsAfterFinished: 60
backoffLimit: 1
restartPolicy: Never
SlimFaas/Job: "true" is required. DefaultVisibility defaults to Private,
NumberParallelJob defaults to 1, DependsOn is comma-separated, and
Schedules uses the same JSON array format as the Kubernetes annotation.
The former local fields parallelism, visibility, dependsOn, and
schedules have been removed and are rejected rather than treated as aliases.
SlimFaas/Function, replica scaling, and event subscription annotations apply
to entries under functions:; Kubernetes Jobs are configured with
SlimFaas/Job and do not subscribe to function events.
Job commands run without a shell. Arguments from /job/{name} are appended to
the configured command; a request cannot replace that executable through
Image. CPU and memory fields remain accepted for API compatibility but are
not enforced in process mode.
The normal distributed queue, leader election, dependencies, parallelism, and
schedules decide when a Job is created. The supervisor makes creation
idempotent by jobFullName, tracks exit status and backoffLimit, and applies
the configured TTL.
Each execution also receives SLIMFAAS_JOB_NAME (the Job configuration name)
and SLIMFAAS_JOB_RUN_NAME (the generated execution name). Because native
processes share the host IP, the supervisor rewrites http:// and ws://
URLs that target cluster.entrypointPort on 127.0.0.1, localhost, or
::1 in the Job command and environment. Those calls pass through a
per-execution loopback gateway so the network map can attribute synchronous
and asynchronous function calls to the exact Job execution.
Use the stable local entrypoint in Job environment variables rather than a direct node port. URLs hard-coded inside the application cannot be rewritten and remain displayed as external callers.
Entrypoint and load balancing
cluster.entrypointPort is the stable TCP entrypoint used by applications and
developers. Each new connection is sent round-robin to a node whose /ready
endpoint succeeds. cluster.nodeHttpPortBase is the first direct node HTTP
port; subsequent nodes use consecutive ports. Interrupted connections are
never replayed; a later connection selects from the current ready set.
Logs and shutdown
local up stays in the foreground, prefixes and aggregates child output, and
writes the same output below state.directory/logs. Every emitted line keeps
its [function/<name>/<replica>], [job/<name>], [process/<name>], or
[node/slimfaas-<index>] prefix.
SlimFaas node logs default to Error in native local mode so function, Job,
and auxiliary-process output remains prominent. Configure the node verbosity
independently when troubleshooting the local cluster:
cluster:
nodeLogLevel: Warning
Accepted values are Trace, Debug, Information, Warning, Error,
Critical, and None. The setting applies from the first node bootstrap log
and covers SlimFaas, SlimData, Raft, and ASP.NET Core. Function log levels
remain controlled by each function's environment, for example:
functions:
fibonacci1:
environment:
Logging__LogLevel__Default: Debug
Console OpenTelemetry exporters are disabled for the managed SlimFaas nodes; functions retain their own telemetry configuration.
Ctrl+C and, on Unix, SIGTERM call configured function shutdown hooks, then
stop functions, Jobs, auxiliary processes, SlimFaas nodes, and their complete
descendant process trees. Forced termination such as kill -9, SIGKILL, or
taskkill /F does not let SlimFaas execute this cleanup and can leave child
processes running.
Processes targeted through debugUrl are launched by the IDE, are not managed
by SlimFaas, and are never stopped by it.
Troubleshooting
- If
http://127.0.0.1:30021/returns404, use the printed entrypoint,http://127.0.0.1:30020/, for the user interface and application traffic. - If a function remains
Starting, inspect its prefixed console output andstate.directory/logs. FordebugUrl, also check that the IDE process is listening on the configured address and that its health endpoint succeeds. - Run
slimfaas local validatewith the same-fand--env-fileoptions aslocal upto identify manifest, interpolation, and port-collision errors. - Use
--cleanwhen an earlier state marker is incompatible with a changed node count or state schema.
The YAML is intentionally static during a run. Detached mode, supervisor high availability, resource enforcement, and network isolation are outside this development-mode scope.