Projects / UnifyOps

UnifyOps: Internal Developer Platform

A platform I built to deploy multi-service systems safely using GitOps, Kubernetes, and strict runtime isolation.

Problem and Constraints

I needed infrastructure that could deploy multiple services consistently, fail safely, and operate without my constant attention.

The alternative was manual deployments, inconsistent configurations across services, and debugging the same problems in different places. I had seen this pattern before — services that worked in isolation but failed in combination, deployments that required tribal knowledge, and outages that cascaded because nothing was isolated.

Constraints

Solo ownership.

I am the only contributor. This means no version negotiation between teams, but also no margin for complexity I cannot debug alone. Every abstraction must pay for itself in reduced cognitive load.

Bare-metal Kubernetes.

The platform runs on home server infrastructure, not managed cloud services. I operate the control plane, the storage layer, and the networking. This rules out cloud-native shortcuts but forces understanding of what actually runs.

Rapid iteration without downtime.

I deploy frequently. The system must handle failed deployments, bad migrations, and broken code without taking down working services. Rollback must be a Git revert, not a runbook.

System Map

UnifyOps spans three repositories with distinct responsibilities:

unifyops

The application monorepo. Contains all service code organized by domain and stack. Each stack implements a three-tier pattern: React frontend, FastAPI API gateway, FastAPI service with PostgreSQL. A shared Python package (unifyops_core) provides logging, authentication, and exception handling across all services.

unifyops-helm

The deployment contract. A single Helm chart that deploys any stack to Kubernetes. Services are not special-cased; they provide values, the chart provides structure. This chart defines health checks, network policies, resource limits, and migration ordering.

unifyops-infra

The GitOps source of truth. Contains environment-specific values files and ArgoCD ApplicationSets. The cluster state is derived from this repository. Deployments happen by updating an image tag and pushing.

This separation allows me to change application behavior, deployment mechanics, or environment configuration independently, without coupling unrelated concerns.

Runtime Architecture

Each stack runs as isolated pods in environment-scoped namespaces (uo-dev, uo-staging, uo-prod):

nginx-ingress → app (:3000) → api (:8002) → service (:8001) → PostgreSQL

Traffic flows are enforced by NetworkPolicy, not convention. The service tier only accepts connections from its API gateway. PostgreSQL only accepts connections from its service. A compromised pod cannot reach adjacent services.

Deployments use sync-wave ordering: database ready (wave 0), migrations complete (wave 1), application pods start (wave 2). This guarantees schema changes apply before code that depends on them.

Platform Contracts

The platform relies on contracts between repositories. Some are explicit and validated; others are implicit and carry risk.

ContractEnforcementRisk
Helm values schemaTemplate validationLow
Health endpointsRuntime probeMedium
Port conventions (3000/8002/8001)Convention onlyMedium
Shared library versionNone (wildcard)High
Image namingConvention across 3 reposHigh
GitOps pathsArgoCD sync failureLow

High-risk contracts are candidates for CI validation if this platform moved beyond solo ownership.

Core Package Design

unifyops_core centralizes cross-cutting concerns that would otherwise be duplicated across every service. It enforces consistency at the code level — not through documentation or convention, but through shared implementation.

ModuleResponsibility
exceptionsTyped exception hierarchy with HTTP semantics
loggingStructured JSON output, sensitive data redaction, service metadata injection
authJWT validation middleware, service-to-service authentication, token caching
clientAsync HTTP client with logging, error handling, credential masking

All services return identical error shapes:

{
  "status_code": 404,
  "message": "User not found",
  "error_id": "550e8400-e29b-41d4-a716-446655440000",
  "error_type": "db_record_not_found",
  "timestamp": "2024-01-01T12:00:00.123456Z"
}

One line — register_exception_handlers(app) — gives every FastAPI app identical error responses. The error_id enables log correlation across services.

Monorepo Structure

A stack is a vertical slice of functionality that can be developed, deployed, and scaled independently.

unifyops/
├── identity/                    # Domain: user identity
│   ├── auth/                    # Stack: authentication
│   │   ├── auth-app/            # React frontend
│   │   ├── auth-api/            # FastAPI gateway
│   │   └── auth-service/        # FastAPI + PostgreSQL
│   └── user/                    # Stack: user profiles
├── platform/                    # Domain: platform infrastructure
│   └── environment/             # Stack: environment management
└── shared/
    └── unifyops_core/           # Cross-stack Python utilities

The CI workflow detects which apps changed through path matching:

  • Changing auth-service builds only auth-service
  • Three apps changed → three parallel build jobs
  • SHA tags are immutable; environment-latest tags enable quick rollback

End-to-End Deploy Flow

A code change flows from commit to running pod in approximately two minutes:

TimeStepActor
T+0sPush to devDeveloper
T+5sChange detectionGitHub Actions
T+30sDocker build (cached)GitHub Actions
T+45sPush to HarborGitHub Actions
T+50sUpdate infra repoGitHub Actions
T+60sArgoCD detects driftArgoCD
T+70sMigration jobKubernetes
T+90sDeployment rolloutKubernetes
T+105sTraffic serving

Failure Containment

  • Build fails: Zero blast radius — no image pushed, no cluster change
  • Image pull fails: Old pod continues serving (maxUnavailable: 0)
  • Migration fails: Deployment blocked, old pods serve with old schema
  • Health check fails: New pod doesn't receive traffic
  • Rollback: Revert image tag in values file, ArgoCD syncs within 3 minutes

Decisions and Tradeoffs

DecisionWhyCostRevisit When
GitOps with ArgoCDAudit trail, drift detection, self-healingAdditional infra repoNever
MonorepoAtomic commits, shared toolingNo per-service versioningMultiple teams need independent releases
Centralized core packageConsistency without documentationServices cannot deviateTeams need autonomy
Three-tier stacksSecurity boundaries via NetworkPolicyThree deploys per stackExtreme internal call volume
Default-deny networkBlast radius limitationExplicit allow requiredNever

Architecture Overview

┌─────────────────────────────────────────────────────────────────────────┐
│                              DEVELOPMENT                                 │
│  ┌─────────────────────┐    ┌─────────────────────┐                     │
│  │      unifyops       │────│   unifyops_core     │                     │
│  │     (monorepo)      │    │  (shared package)   │                     │
│  └──────────┬──────────┘    └─────────────────────┘                     │
└─────────────┼───────────────────────────────────────────────────────────┘
              │ git push
              ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                                 BUILD                                    │
│  ┌─────────────────────┐         ┌─────────────────────┐                │
│  │   GitHub Actions    │────────▶│   Harbor Registry   │                │
│  │  (build & push)     │         │  (images + charts)  │                │
│  └──────────┬──────────┘         └─────────────────────┘                │
└─────────────┼───────────────────────────────────────────────────────────┘
              │ update tag
              ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                                GITOPS                                    │
│  ┌─────────────────────┐    ┌─────────────────────┐                     │
│  │   unifyops-infra    │───▶│      ArgoCD         │◀───unifyops-helm    │
│  │   (desired state)   │    │   (reconcile)       │   (templates)       │
│  └─────────────────────┘    └──────────┬──────────┘                     │
└─────────────────────────────────────────┼───────────────────────────────┘
                                          │ sync
                                          ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                           KUBERNETES CLUSTER                             │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                    Namespace: uo-{env}                            │  │
│  │   ┌─────────┐     ┌─────────┐     ┌─────────────┐     ┌────────┐ │  │
│  │   │   app   │────▶│   api   │────▶│   service   │────▶│ postgres│ │  │
│  │   │  :3000  │     │  :8002  │     │    :8001    │     │  :5432  │ │  │
│  │   └─────────┘     └─────────┘     └─────────────┘     └────────┘ │  │
│  └───────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘

Unidirectional flow from monorepo to cluster. ArgoCD acts as the control plane, reconciling desired state from Git. unifyops_core ensures runtime consistency across all services.

Outcomes

  • Deployment velocity: Code change to production traffic in under two minutes, with zero-downtime rolling updates
  • Failure containment: Build failures have zero blast radius; runtime failures are isolated to the affected stack
  • Operational consistency: All services log identically, return identical error shapes, and authenticate identically
  • Auditability: Every deployment is a Git commit; every rollback is a revert
  • Scalability: Adding a new stack is copying a template and updating configuration

These outcomes matter because the platform is designed to be operated by one person, safely, on infrastructure I fully control.

What I Would Improve Next

  • Versioned core package releases: Publish to Harbor PyPI, pin explicit versions in services
  • Contract validation in CI: Validate health endpoints, ports, and image naming before deployment
  • Observability depth: Add OpenTelemetry instrumentation and Grafana dashboards
  • Multi-cluster support: Extend ArgoCD for disaster recovery or geographic distribution

What I Owned

Full vertical ownership. I designed the architecture, wrote the code, built the CI/CD pipelines, configured ArgoCD, and operate the Kubernetes cluster.

Application Code

FastAPI services, React frontends, SQLAlchemy models, Alembic migrations

Shared Infrastructure

unifyops_core package (4,400 lines) — logging, auth, exceptions, HTTP client

CI/CD

GitHub Actions with change detection, matrix builds, Harbor push, GitOps updates

Kubernetes Deployment

Helm chart, sync-wave ordering, NetworkPolicy, health probes, HPA

GitOps

ArgoCD ApplicationSets, environment namespaces, sealed secrets

Operations

Monitoring, log aggregation, incident response, rollback execution

This is not a team project I contributed to. I built it.

UnifyOps is a platform I built to solve my own infrastructure problems. It enforces consistency through shared code, deploys through Git, contains failures by design, and operates without my constant attention.

Every decision reflects real constraints: solo ownership, bare-metal infrastructure, and the need for safe rapid iteration. I acknowledge what I traded away — flexibility, per-service versioning, and the ability to deviate from platform patterns.

This is how I build and operate software.

© 2026 Jared Kominsky