charts/sinas
that deploys the full platform — backend, queue workers, scheduler, CDC
worker, console — plus bundled PostgreSQL, Redis, and ClickHouse.
Untrusted code (untrusted functions and agent code execution) runs in
ephemeral hardened Pods: one pod per execution, created through the
Kubernetes API and deleted afterwards. No Docker socket, no privileged
Docker-in-Docker. Admin-approved (shared_pool) functions run in-process in
the queue workers.
This works on any conformant cluster — kind, k3s, Scaleway Kapsule, GKE,
AWS EKS (EKS nodes run containerd and have no Docker socket, which is
exactly why the pod-based executor exists).
Install
The chart is published with every release — no checkout required. Install it straight from the registry (replace0.3.0 with the version you want):
sinas-<version>.tgz you can install
from directly, and a checkout works too:
How sandbox pods are secured
Each execution pod is created with the same hardening as the Docker sandbox:- all capabilities dropped (
CHOWN/SETUID/SETGIDadded back), no privilege escalation,RuntimeDefaultseccomp profile - memory / CPU / ephemeral-storage limits from
MAX_FUNCTION_* - in-memory
/tmp(100Mi), single-use, deleted after the execution,activeDeadlineSecondsas a leak backstop - no ServiceAccount token — sandbox pods cannot talk to the Kubernetes API
- a NetworkPolicy that allows DNS and internet egress only: sandbox pods can never reach cluster-internal services (PostgreSQL, Redis, other namespaces)
pods + pods/exec in the release namespace — that is the entire privilege
surface replacing the Docker socket.
NetworkPolicies require a CNI that enforces them (Cilium, Calico, kindnetd on
recent kind, VPC CNI with a policy engine on EKS). Without enforcement the
deployment still works, but sandbox pods are not network-isolated.
Executor image and cold-start latency
Sandbox pods runexecutor.image. By default, platform dependencies (the
admin-managed package list) are pip installed into each pod at creation,
which adds seconds to every sandbox execution. For production, bake them in:
Advanced: per-client node scheduling
These aren’t exposed as values in the bundledcharts/sinas chart — they’re
env vars for operators building their own multi-tenant deployment tooling
around the k8s_pod executor (e.g. a chart that provisions one release per
customer). Sinas doesn’t decide scheduling policy itself; it just applies
whatever it’s given, so the same knobs work whether you want every
customer’s sandbox pods spread across dedicated nodes or packed together on
shared ones — that choice lives entirely in your own tooling, not here.
All four are no-ops by default, matching the behavior described above with
no scheduling constraint at all.
Sizing under namespace quotas
Sandbox pods are created on demand, per execution, in the release namespace — so they compete with the platform’s own pods for any namespaceResourceQuota. Plan the quota headroom explicitly:
- Sandbox pod size is set by the executor, with
requests == limits:MAX_FUNCTION_MEMORY(MiB, default512) andMAX_FUNCTION_CPU(cores, default1.0), plus an ephemeral-storage limit fromMAX_FUNCTION_STORAGE. Pass them via the chart’sextraEnvto shrink each execution’s footprint, e.g.MAX_FUNCTION_MEMORY=192,MAX_FUNCTION_CPU=0.5. - Concurrency ceiling ≈ what’s left of the quota after the platform pods,
divided by one sandbox pod’s request. Example: with a
requests.memory: 3Giquota and the default chart components requesting ~1.4Gi, a 192Mi sandbox pod allows ~8 concurrent executions; a 512Mi one allows ~3. Checkrequests.cpuand thepods:count the same way — the binding constraint is whichever runs out first. - Failure mode: when the quota is exhausted, pod creation is rejected and
the execution fails with a quota error — reduce sandbox size, raise the
quota, or lower worker concurrency (
queueWorker/queueAgentvalues) to bound simultaneous executions. - Trusted (
inprocess) code has no per-function memory cap — it runs inside the queue-worker process, so the worker’sresources.limits.memoryis the tenant’s burst envelope forshared_poolfunctions (worker baseline + concurrency × per-call allocation). Size it accordingly. - Datastores (postgres, redis, pgbouncer) set no container resources in
the chart; on clusters with a
LimitRangethey inherit its defaults, which count against the quota too. Give them explicit values in your own manifest patches if you need deterministic accounting.
resources are values — override any of them
per deployment.
Compact profile (density deployments)
For packing several small instances onto one box (e.g. 3 instances on a 4GB node), drop ClickHouse and size requests near measured idle usage (backend idles ~175MB, workers ~90–120MB):queueWorker.concurrency low so tenants can’t burst past it, or set
SANDBOX_EXECUTOR=disabled for trusted-only tiers.
With clickhouse.enabled: false the backend detects the empty
CLICKHOUSE_HOST and disables logging cleanly (no reconnect attempts); the
Logs pages return empty results.
Key values
Limitations
input()/ human-in-the-loop pauses are unavailable: sandbox pods are single-use (sandbox mode has always rejectedinput()), and theinprocesstrusted executor is run-to-completion. Durable pause/resume is tracked in issue #79.- The bundled datastores are convenience-grade. For production, point
DATABASE_URL-family settings at managed services (e.g. RDS, ElastiCache) and disable the bundled StatefulSets.