Skip to main content

Self-Hosted Deployment

Deploy the Mubeen Solution on Your Own Infrastructure · October 2026

This guide is for teams that run both Mubeen's data plane and control plane, every container, not just the parts that touch data, inside their own cloud or on their own servers, instead of calling Mubeen's hosted API (see Architecture Overview for the hosted model). You own the compute, networking, and data at rest; Mubeen ships the container images.

Three exceptions to full self-containment, each covered in detail further down this page: the Dashboard's calls to an external LLM provider and SMTP relay, outbound license validation, and Mubeen's standing support VPN into the Data Layer (see Mubeen Support Access).

What You'll Deploy​

ComponentRoleYou provide
WAFFilters traffic before it reaches a load balancer, on both the public and the internal edgeYour cloud's WAF (AWS WAF, Cloudflare, ModSecurity, etc.)
Public Load BalancerTLS termination for the one public endpoint: fingerprint traffic onlyYour cloud's LB (ALB, NLB, nginx, etc.)
Private Load BalancerRoutes internal traffic to Rule Engine and Dashboard; never faces the public internetYour cloud's internal LB
Fingerprint CollectorReceives device fingerprints, issues device_idContainer host + config; outbound internet for real-time device/IP reputation lookups against Mubeen's threat-intelligence feed
Rule EngineScores risk-context payloads, returns decisions; calls ML Scoring for model-based scoringContainer host + config
ML ScoringRuns the ML model inference behind Rule Engine's scoring calls. Nothing else reaches it: no LB, no other callerContainer host + config; no internet needed, models ship inside the image
DashboardWeb UI for reviewing decisions and managing rules; calls an LLM for AI-assisted analysis; sends OTP login codes, reports, and notification emailsContainer host + config; an OpenAI-compatible LLM API key; SMTP/email provider credentials
DB Schema ManagerApplies Postgres schema migrations. Talks only to Postgres, with elevated/DDL access; not part of the request path, not reachable by anything elseContainer host + config; a superuser or DDL-capable Postgres role (distinct from the app containers' least-privilege roles)
PostgresPrimary datastore for decisions, rules, and dashboard stateManaged or self-run Postgres instance
ValkeyCache / queue for the app containersManaged or self-run Valkey instance
Grafana AlloyCollects metrics and logs from all app containersContainer host
GrafanaDashboards and alerting on top of Alloy's telemetry; reached over your internal network, not via either LBContainer host; outbound internet is optional, only needed for the plugin catalog, update checks, or alerting to an external service (Slack, PagerDuty, email)
MetabaseAd-hoc analytics / BI on top of Postgres; reached over your internal network, not via either LBContainer host; outbound internet is optional, only needed for map-tile visualizations, version checks, or (unless disabled) anonymous usage stats
Dev MachineML training / model development against real dataA host inside your cloud, reachable only via VPN (not a public port), with SSH access and read-only DB access to Postgres; outbound internet scoped to package/dependency installs (e.g. via an egress proxy or allowlist to package registries)
Support Bastion / VPN GatewayStanding entry point for Mubeen support to reach Postgres and Valkey. Container log/exec access is a separate mechanism (e.g. docker exec/kubectl exec over the same bastion host), not a network pathA VPN gateway or bastion host inside your VPC, Mubeen-held credentials

Two Ingress Paths, Kept Apart​

  • Public path: your users → WAF → Public Load Balancer → Fingerprint Collector only. This is the one path that faces the open internet. In practice your WAF is usually integrated with the load balancer or CDN itself (e.g. AWS WAF attached to an ALB, Cloudflare) and inspects the decrypted HTTP request. It isn't a separate network hop that only sees raw TLS ciphertext. The diagram shows it as a distinct box for clarity, not to imply a literal packet-order requirement.
  • Private path: your app servers → WAF → Private Load Balancer → Rule Engine, and your employees → WAF → Private Load Balancer → Dashboard. This path should never be reachable from the public internet in the first place; the internal WAF is defence in depth on top of that, not the thing keeping it private. It catches the same request-level attacks (injection, malformed payloads, credential stuffing against the Dashboard login) from a compromised app server or employee workstation. As on the public edge, it is normally a policy attached to the internal load balancer rather than a separate network hop. The Dashboard also calls the Rule Engine directly (to manage rules), which does not pass through the Private LB or its WAF.

Grafana and Metabase aren't on either path. They're reached directly over your own internal network/VPN, not routed through a load balancer. Treat them as additional private endpoints that need their own access control, not as an implicit extension of the Private LB.

Topology​

Solid arrows are the request path, dashed arrows are telemetry, state, migrations, or read access, and thick purple arrows are privileged access (SSH/DB/VPN). Blue boxes are components developed in-house by Mubeen; the slate data stores are part of your infrastructure. Outbound-internet requirements are noted in the node labels rather than drawn as separate arrows, to keep the figure readable. Traffic on the private path also passes a WAF on the internal edge (defence in depth before the Private LB); the Dashboard's direct calls to the Rule Engine do not pass through the Private LB or its WAF.

YOUR CLOUD / VPCDATA · SEPARATE SUBNETHTTPSFP ONLYPRIVATEVPNSCOREMANAGES RULESMIGRATIONSSTATEREADSSSH · DBTELEMETRYTELEMETRYYour Userspublic internetApp Serversprivate networkEmployeesprivate networkMubeen Supportstanding VPN clientWAFedge filterPrivate LBno public internetSupport BastionMubeen-held credsPublic LBfingerprint trafficonlyDB Schema ManagerDDL roleno internetFingerprintCollectorneeds internetRule Engineno internet neededML Scoringno internet neededDashboardneeds internet:SMTP · LLMPostgresstate · rulesValkeycache · queuesGrafana Alloycollects from all servicesGrafanainternet optionalMetabaseinternet optionalDev MachineVPN + SSH · DB accessscoped internetLEGENDRequest pathTelemetry / state / migrationsPrivileged access (VPN / SSH / DB)Developed in-house
Request pathTelemetry / state / migrationsPrivileged access (VPN / SSH / DB)Developed in-house
Solid arrows are the request path, dashed arrows are telemetry or read access, and thick purple arrows are privileged access (SSH/DB/VPN).

Container Reference​

ContainerDepends on
fingerprint-svcPostgres, Valkey
rule-enginePostgres, Valkey, ml-scoring
ml-scoring— (no datastore access; called by rule-engine only)
dashboard-uiPostgres, Valkey, rule-engine
db-schema-managerPostgres (not network-reachable; runs migrations then exits)
grafana-alloyfingerprint-svc, rule-engine, ml-scoring, dashboard-ui
grafanaAlloy
metabasePostgres

Network Access Requirements​

Firewall/security-group rules to plan for. Rule Engine and Dashboard are never reachable from the public internet: only the public WAF + Public LB + Fingerprint Collector path is.

ContainerInboundOutbound to internetNotes
WAF (public edge)Internet (public)—Public entry point: public path only
Public Load BalancerWAF (public edge)—Routes to Fingerprint Collector only
WAF (internal edge)Your app servers, your employees (private network)—No public inbound; filters internal traffic before the Private LB
Private Load BalancerWAF (internal edge)—No public internet exposure; routes to Rule Engine + Dashboard
Fingerprint CollectorPublic LB onlyRequiredReal-time device/IP reputation lookups against Mubeen's threat-intelligence feed
Rule EnginePrivate LB only (your app servers); also called directly by DashboardNot requiredOnly talks to Postgres, Valkey, and ML Scoring: no outbound internet needed; no public inbound
ML ScoringRule Engine only: no LB, no other caller, no public inboundNot requiredModels ship inside the image; do not expose this container to the Private LB or any other caller
DashboardPrivate LB only (your employees)RequiredOutbound includes your LLM provider's API endpoint (OpenAI-compatible: OpenAI, Azure OpenAI, or a self-hosted/proxied endpoint) and your SMTP relay / email provider (OTP codes, reports, notifications)
DB Schema ManagerNot reachable by anything: runs migrations, then exits—No internet needed; migrations ship inside the image
PostgresFingerprint Collector, Rule Engine, Dashboard, Metabase, Dev Machine, Support Bastion, DB Schema Manager—No public internet exposure in either direction. Support Bastion reaches it over a private VPN link; Dev Machine is inside the same cloud. Every connection uses sslmode=require
ValkeyFingerprint Collector, Rule Engine, Dashboard, Support Bastion—No public internet exposure in either direction
Grafana AlloyFingerprint Collector, Rule Engine, ML Scoring, Dashboard (telemetry)—
GrafanaAlloy, internal users over your networkOptionalNot routed through either load balancer. Only needed for the plugin catalog, update checks, or alerting to an external service
MetabaseInternal users over your networkOptionalNot routed through either load balancer. Only needed for map-tile visualizations, version checks, or (unless disabled) anonymous usage stats
Dev MachineSSH via VPNRequired, scopedOutbound to package/dependency registries only (via an egress proxy or allowlist, not open internet), plus read-only DB access to Postgres, for ML training
Support Bastion / VPN GatewayStanding VPN from Mubeen support—Mubeen-held credentials; scoped support access to Postgres and Valkey only, non-superuser (see Mubeen Support Access). Container log/exec access is separate (not a network path). This is a standing, always-on connection, not a per-incident grant

Mubeen Support Access​

Mubeen reaches your self-hosted deployment through a standing VPN connection to a bastion host inside your VPC, using Mubeen-held credentials, not a per-incident grant. This is how Mubeen debugs issues and applies updates without needing inbound internet access to your environment. The bastion's network reach is Postgres and Valkey only. It is not a general path into the app containers; log/exec access to those is a separate mechanism layered on top of the same host.

  • No shared credentials: each Mubeen support engineer connects with their own named identity; never a single shared VPN/DB credential used by the whole support team
  • MFA / hardware-key auth: required at the VPN, not just a password
  • IP allowlist: restrict the VPN endpoint to Mubeen's published support egress ranges; request the current list from your Mubeen account contact before provisioning
  • Time-boxed credentials: short-lived SSH certs or DB tokens, even though the VPN connection itself is standing
  • Least-privilege access: the bastion's Postgres access should be a non-superuser role scoped to what support needs, not full admin (no DROP/ALTER, no access to unrelated schemas); its Valkey access should be a similarly scoped ACL user, not unrestricted CONFIG/FLUSHALL

If your organization can't accept a standing connection under these terms, ask Mubeen about a per-incident, customer-approved access model instead (bastion opened on a support ticket, closed after). This has slower time-to-support but a much smaller blast radius.

Rough Sizing Guide​

Starting points only, not benchmarked by Mubeen. Load-test against your own traffic pattern before committing to a tier.

All Mubeen images are built for linux/amd64 (x86-64). Don't provision ARM-based instances (e.g. AWS Graviton) for any Mubeen container unless you've confirmed an ARM image is available.

Component1M requests/day5M requests/day
Fingerprint Collector2 vCPU / 4 GB8 vCPU / 16 GB
Rule Engine2 vCPU / 4 GB8 vCPU / 16 GB
ML Scoring2 vCPU / 4 GB8 vCPU / 16 GB
Dashboard2 vCPU / 4 GB4 vCPU / 16 GB
Postgres2 vCPU / 16 GB8 vCPU / 64 GB
Valkey2 vCPU / 8 GB4 vCPU / 32 GB

DB Schema Manager isn't in this table. It's a one-shot job, not a long-running service, so sizing it isn't traffic-sensitive the way the rest of this list is. Grafana Alloy, Grafana, Metabase, the Support Bastion, and the Dev Machine aren't covered either; size those against your own retention and usage needs rather than request volume.

Dashboard is capped at 4 vCPU rather than scaling 1:1 with the others. It's an internal UI, not on the hot request path, so it doesn't need to grow with fingerprint/scoring traffic.

The sizing above is per node. Recommended topology, not a single instance of each:

  • Postgres: 1 writer + 1 reader, each sized per the table above
  • Valkey: clustered with replication (mirroring), not a single node

Before You Provision​

Infrastructure Basics​

  • Instance architecture: linux/amd64 (x86-64) only. Don't provision ARM instances (e.g. AWS Graviton) for Mubeen containers
  • Time zone: set every host and container to UTC
  • Resource sizing: see Rough Sizing Guide above for starting points

Network & Edge​

  • WAF rules: baseline rule set for the Public LB's fingerprint endpoint and for the Private LB's internal endpoint, confirmed with Mubeen
  • TLS certificates: for both the Public LB's public endpoint and the Private LB's internal endpoint
  • DNS: public hostname for fingerprint traffic, private/internal hostnames for the dashboard and Rule Engine
  • Outbound connectivity: see Network Access Requirements; confirm exact destination hosts to allowlist with Mubeen (license validation, image pulls); route this traffic through a NAT gateway or egress proxy so the allowlist is actually enforced, not just documented
  • Network segmentation: three subnets, not two. Separate the App Layer (Fingerprint Collector, Rule Engine, ML Scoring, Dashboard, DB Schema Manager), the Data Layer (Postgres, Valkey), and a Management Layer (Support Bastion, Dev Machine, Grafana Alloy, Grafana, Metabase), with security groups that block direct east-west traffic beyond what's actually needed (the internet-facing Fingerprint Collector shouldn't be able to reach the Rule Engine directly, and the Management Layer's egress to the Data Layer should be scoped to Postgres/Valkey only, not the App Layer)
  • Rule Engine + Dashboard isolation: confirm both sit only behind the Private LB, with no path from the public internet or the WAF-fronted listener
  • ML Scoring isolation: confirm it isn't reachable from either load balancer or anything other than Rule Engine

Data & Backups​

  • Backup strategy: for Postgres (decisions/rules data is not disposable)
  • Schema migration process: decide how db-schema-manager runs in your environment (manual step, CI/CD job, Helm hook) and who holds its elevated DB role
  • Per-service database roles: least-privilege Postgres roles per container, not one shared superuser credential; db-schema-manager's DDL role is the one deliberate exception
  • Encryption at rest (required, not optional): the Postgres and Valkey volumes must both be encrypted (cloud-disk encryption or equivalent); this applies to backups too
  • Encryption in transit (every hop, not just the public edge):
    • Public LB and Private LB → app containers (backend/target-group TLS, not just the client-facing certificate)
    • App containers → Postgres (sslmode=require)
    • App containers → Valkey (rediss://)
    • Dashboard → Rule Engine, and Rule Engine → ML Scoring (https:// on both, not plain http://)
    • Support Bastion → Postgres and Valkey (sslmode=verify-full with a pinned CA, not just require; require encrypts but doesn't validate the server certificate, and a standing vendor VPN connection is exactly the hop that shouldn't rely on "inside the VPC" trust alone)
    • Dev Machine → Postgres (sslmode=verify-full with a pinned CA, same reasoning as the bastion)
    • Grafana Alloy → Grafana, and app containers push telemetry to Alloy's ingest endpoint

Access Control & Hardening​

  • Grafana/Metabase hardening: change Grafana's default admin password before first boot; keep both reachable only over your internal network, never a public port
  • Dev machine access: SSH gated behind your VPN (not exposed directly to the internet), a scoped read-only DB role for ML training instead of full production credentials, and outbound internet restricted to package/dependency registries (egress proxy or allowlist), not open access
  • Support bastion: provision the VPN gateway Mubeen support connects through; see Mubeen Support Access for the required scoping and MFA controls

Access & Credentials​

  • Container registry access: credentials to pull Mubeen's images
  • LLM API key: an OpenAI-compatible API key/endpoint for the Dashboard's AI-assisted analysis (OpenAI, Azure OpenAI, or a self-hosted/proxied OpenAI-compatible endpoint)
  • Email/SMTP credentials: for the Dashboard to send OTP login codes, reports, and notifications (your own SMTP server, or a provider like SES/SendGrid/Postmark)
  • Secrets management: DB credentials, API keys, the LLM key, and SMTP credentials

Where to Go Next​