Skip to content

Internal Developer Platform (IDP)

Minnova's approach to developer self-service for application deployment.

Last updated: 2025-12-28


Problem Statement

Current state: DevOps writes K8s manifests, manually provisions databases, handles all deployments.

Target state: Developers self-service deploy applications with databases, preview environments auto-created on PRs.


Chosen Approach: GitOps-Native IDP

After evaluating external tools (Devtron, Kubero, Backstage), we chose to build a lightweight internal IDP using existing infrastructure:

Component Solution
App Deployment ArgoCD ApplicationSet + minnova-app Helm chart
Database Provisioning CloudNative-PG via Helm chart
Preview Environments ArgoCD Pull Request Generator
Configuration Simple YAML in app repos
Secrets SOPS in app repos (shared key)

Why Not External Tools?

Tool Issue
Devtron Conflicts with existing ArgoCD, complex SSO, heavy dependencies
Kubero CRD-based (not Git-centric), own database addons (not CNPG)
Backstage Too complex for small teams

ArgoCD Git Authentication

Each deployment has its own ArgoCD instance with its own credentials. This ensures: - Client credentials stay in client's deployment - No cross-client secret sharing - Client can revoke access independently

Git Provider Recommended Auth
GitHub GitHub App (single credential for all org repos)
Forgejo/Gitea Deploy Keys (SSH key per repo)
GitLab Project Access Token

Onboarding flow: 1. Client creates GitHub App in their org (Contents: Read) 2. Credentials stored in their deployment's secrets 3. Their ArgoCD uses their credentials

See ArgoCD docs: GitHub App


How It Works

1. App Registry

Each app has a simple YAML file in the infra repo:

# deployments/minnova/apps/oracle.yaml
name: oracle
repo: https://github.com/minnova-io/oracle.git

To add a new app: Developer opens PR to add a new file apps/<name>.yaml. Platform team reviews and merges.

2. App Configuration

Each app has a .minnova/app.yaml in its repo:

# .minnova/app.yaml
name: oracle

image:
  repository: registry.minnova.io/oracle

port: 4000

database:
  enabled: true
  size: 5Gi

environments:
  dev:
    replicas: 1
    host: oracle.dev.minnova.io
  prod:
    replicas: 3
    host: oracle.minnova.io

env:
  LOG_LEVEL: info

Developers own this config. Changes trigger redeployment.

3. Secrets

App secrets live in the app repo, encrypted with SOPS:

# .minnova/secrets/prod.enc.yaml
apiVersion: v1
kind: Secret
metadata:
  name: oracle-secrets
stringData:
  SECRET_KEY_BASE: ENC[AES256_GCM,data:...,type:str]
  API_KEY: ENC[AES256_GCM,data:...,type:str]

Apps use the platform's SOPS Age key for encryption. This allows:

  • Secrets version-controlled with code
  • Developers manage their own secrets
  • Platform team controls the key

4. ArgoCD Generates Deployments

The ApplicationSet reads registry + app configs and generates:

  • One ArgoCD Application per app per environment
  • Each uses the minnova-app Helm chart
  • Secrets are decrypted by SOPS operator in cluster

Preview Environments

When a PR is opened, ArgoCD automatically:

  1. Creates a preview namespace
  2. Deploys the app with PR-specific config
  3. Creates preview database (if enabled)
  4. Exposes at app-pr-123.preview.minnova.io

When PR is closed/merged:

  • Namespace and all resources are deleted

Developer Workflow

Adding a New App

  1. Create .minnova/app.yaml in your repo
  2. Add secrets to .minnova/secrets/ (encrypted with sops)
  3. Open PR to platform registry to register the app
  4. Once merged, ArgoCD deploys automatically

Updating an App

  1. Update .minnova/app.yaml or push new image tag
  2. ArgoCD detects change and syncs

Adding Secrets

# Encrypt with platform's Age key
sops --encrypt --age age1... secret.yaml > .minnova/secrets/prod.enc.yaml
git add .minnova/secrets/
git commit -m "add production secrets"

Database Provisioning

When database.enabled: true, the platform:

  1. Creates a CloudNative-PG Cluster
  2. Configures automated backups to R2
  3. Injects DATABASE_URL into the app

Developers just set:

database:
  enabled: true
  size: 5Gi

Implementation Status

  • minnova-app Helm chart structure
  • ArgoCD ApplicationSet for registry
  • Preview environment ApplicationSet
  • Test with first app (oracle)
  • Document SOPS key sharing process

References