Installation
In Falcone is packaged as a single umbrella Helm chart (charts/in-falcone) plus a docker-compose stack (tests/env/docker-compose.yml) for local development. This page covers four deployment targets:
- Docker Compose — fastest path, for development
- Kubernetes — Ingress-based exposure
- OpenShift — Routes +
restricted-v2SCC - Air-gapped — private registry, no internet
NOTE
The chart is an umbrella: each platform component (gateway, identity, databases, storage, events, functions, control plane, console, observability) is a dependency wrapped by a shared component-wrapper subchart and toggled with <component>.enabled. You can run everything in one cluster or point a component at an external managed service.
Prerequisites
| Target | Needs |
|---|---|
| Docker Compose | Docker / Docker Compose v2 |
| Kubernetes | A cluster (1.27+), kubectl, Helm 3.14+ |
| OpenShift | OpenShift 4.x, oc, Helm 3.14+ |
| Air-gapped | A reachable private OCI registry + the chart/images mirrored into it |
The umbrella chart pulls file-based subcharts, so always build dependencies first:
helm dependency build charts/in-falconeValues layering
The chart is designed to be configured by layering values files, applied left-to-right (later files win). The recommended order (surfaced in the chart's NOTES.txt) is:
- common — shared defaults
- environment —
dev.yaml/staging.yaml/prod.yaml - customer —
customer-reference.yaml(per-customer overrides) - platform —
platform-kubernetes.yaml/platform-openshift.yaml - airgap —
airgap.yaml(only when air-gapped) - local override —
local.example.yaml(last-mile, never committed secrets)
Deployment profiles under charts/in-falcone/values/profiles/ size the install:
| Profile | Use |
|---|---|
all-in-one.yaml | Single-node / demo — every component in-cluster |
standard.yaml | Typical production split |
ha.yaml | High-availability (replicated components) |
Set the active profile with deployment.profile and layer the matching file.
Docker Compose (local)
The compose stack in tests/env/docker-compose.yml brings up the real backends the platform runs against — ideal for development and for running the test suites against live services.
cd tests/env
docker compose up -dIt starts:
| Service | Image | Purpose |
|---|---|---|
postgres | postgres:16-alpine | Relational backend (with tenant RLS) |
mongodb | mongo:7 (--replSet rs0) | Document backend + change streams |
keycloak | quay.io/keycloak/keycloak:26.0 | Identity (OIDC), realm auto-imported |
redpanda | redpandadata/redpanda:v24.2.7 | Kafka-compatible event bus |
minio | minio/minio:latest | S3-compatible object storage |
vault | hashicorp/vault:1.18 (dev) | Secret backend |
apisix | apache/apisix:3.9.1-debian | API gateway |
IMPORTANT
MongoDB runs as a single-node replica set (rs0) because change streams (used by realtime) require one. The first time you start a fresh data directory you may need to initiate it:
docker compose exec mongodb mongosh --eval \
'rs.initiate({_id:"rs0",members:[{_id:0,host:"mongodb:27017"}]})'Tear down (and wipe volumes):
docker compose down -vKubernetes
Use the Ingress exposure profile. It assumes an ingress controller (e.g. ingress-nginx) is installed.
helm dependency build charts/in-falcone
helm upgrade --install falcone charts/in-falcone \
--namespace falcone --create-namespace \
-f charts/in-falcone/values/prod.yaml \
-f charts/in-falcone/values/platform-kubernetes.yaml \
-f charts/in-falcone/values/profiles/standard.yamlThe platform-kubernetes.yaml profile sets:
platform:
target: kubernetes
network:
exposureKind: Ingress # publish via Ingress objects
securityProfile: restricted
publicSurface:
ingress:
className: nginx
annotations:
kubernetes.io/ingress.class: nginxAfter install, the chart prints the public endpoints it created:
- API —
https://<api-host>/ - Identity —
https://<identity-host>/ - Realtime —
https://<realtime-host>/ - Console —
https://<console-host>/
Set the hostnames under publicSurface.hostnames.* and TLS under publicSurface.tls.mode. A bootstrap job (<release>-bootstrap) runs on install to reconcile gateway routes, the identity realm and the initial platform configuration (guarded by a lock ConfigMap so it is idempotent).
Watch it converge:
kubectl -n falcone rollout status deploy --timeout=300s
kubectl -n falcone get podsOpenShift
OpenShift uses Routes instead of Ingress and a stricter security context. Layer platform-openshift.yaml:
helm dependency build charts/in-falcone
helm upgrade --install falcone charts/in-falcone \
--namespace falcone --create-namespace \
-f charts/in-falcone/values/prod.yaml \
-f charts/in-falcone/values/platform-openshift.yaml \
-f charts/in-falcone/values/profiles/standard.yamlThe OpenShift profile sets:
platform:
target: openshift
network:
exposureKind: Route # publish via OpenShift Routes
securityProfile: restricted-v2
openshift:
enabled: true
routeAnnotations:
haproxy.router.openshift.io/timeout: 30s
apisix:
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]It drops empty podSecurityContext blocks so OpenShift can inject the namespace's assigned UID range, and applies restricted-v2-compatible container security contexts. Routes are annotated with an HAProxy timeout suitable for the realtime SSE endpoints.
TIP
The platform image references can be retargeted to an OpenShift-internal registry (e.g. Harbor) the same way the air-gapped profile does — see below.
Air-gapped
For clusters with no internet access, mirror all images into a private registry and layer airgap.yaml. It rewrites every component's image repository to the private registry and wires the pull secret + CA bundle:
global:
airgap:
enabled: true
privateRegistry:
enabled: true
registry: registry.airgap.in-falcone.local
pullSecretNames: [in-falcone-registry]
caBundleConfigMap: in-falcone-registry-ca
imagePullSecrets:
- name: in-falcone-registry
imageRegistry: registry.airgap.in-falcone.local
apisix: { image: { repository: registry.airgap.in-falcone.local/apache/apisix } }
keycloak: { image: { repository: registry.airgap.in-falcone.local/keycloak/keycloak } }
postgresql:{ image: { repository: registry.airgap.in-falcone.local/bitnami/postgresql } }
mongodb: { image: { repository: registry.airgap.in-falcone.local/bitnami/mongodb } }
kafka: { image: { repository: registry.airgap.in-falcone.local/bitnami/kafka } }
storage: { image: { repository: registry.airgap.in-falcone.local/minio/minio } }
controlPlane: { image: { repository: registry.airgap.in-falcone.local/example/in-falcone-control-plane } }
webConsole: { image: { repository: registry.airgap.in-falcone.local/example/in-falcone-web-console } }Workflow:
- Mirror images into
registry.airgap.in-falcone.local(useskopeo copyor your registry's import tooling). - Create the pull secret + CA configmap referenced above in the target namespace.
- Install, layering the airgap file last (before any local override):
helm dependency build charts/in-falcone
helm upgrade --install falcone charts/in-falcone \
--namespace falcone --create-namespace \
-f charts/in-falcone/values/prod.yaml \
-f charts/in-falcone/values/platform-kubernetes.yaml \
-f charts/in-falcone/values/profiles/standard.yaml \
-f charts/in-falcone/values/airgap.yamlFor OpenShift air-gapped installs, swap platform-kubernetes.yaml for platform-openshift.yaml.
Verifying the install
# all workloads ready
kubectl -n falcone get pods
# the bootstrap job completed
kubectl -n falcone get job -l app.kubernetes.io/component=bootstrap
# reach the console
open https://<console-host>/Then continue to the Quickstart to create your first tenant and app.
Troubleshooting
helm upgradefails values schema validation — the chart ships a strictvalues.schema.json. If you are iterating on a partial values set, add--skip-schema-validationto the Helm command.- MongoDB realtime not delivering — ensure MongoDB is a replica set; change streams (and therefore realtime) require it.
- Air-gapped images
ImagePullBackOff— confirm the pull secret name matchesglobal.imagePullSecretsand the CA bundle configmap exists in the namespace.