> ## Documentation Index
> Fetch the complete documentation index at: https://kubestarterkit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bootstrapping a New Service

> Add a new application to the platform

## Overview

This guide walks through adding a new service to the platform. You'll create the application code, Dockerfile, Kubernetes manifests, and CI/CD configuration.

## Create the Service

<Steps>
  <Step title="Create the service directory">
    ```bash theme={null}
    mkdir -p services/my-new-service
    cd services/my-new-service
    ```
  </Step>

  <Step title="Add your application code">
    Create your application. For example, a Go service:

    ```bash theme={null}
    go mod init my-new-service
    mkdir cmd
    ```

    Create `cmd/main.go`:

    ```go theme={null}
    package main

    import (
        "log"
        "net/http"
    )

    func main() {
        http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
            w.WriteHeader(http.StatusOK)
            w.Write([]byte("ok"))
        })

        log.Println("Starting server on :8080")
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
    ```
  </Step>

  <Step title="Create a Dockerfile">
    Create `Dockerfile`:

    ```dockerfile theme={null}
    FROM golang:1.23-alpine AS builder
    WORKDIR /app
    COPY go.mod go.sum ./
    RUN go mod download
    COPY . .
    RUN CGO_ENABLED=0 go build -o server ./cmd

    FROM gcr.io/distroless/static-debian12:nonroot
    COPY --from=builder /app/server /server
    EXPOSE 8080
    ENTRYPOINT ["/server"]
    ```
  </Step>

  <Step title="Create mise.toml for service configuration">
    Create `mise.toml`:

    ```toml theme={null}
    [env]
    # Replace with your AWS account ID and region
    IMAGE_REPO = "<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/services/my-new-service"
    SERVICE_RELEASE_TAG = "services/my-new-service"

    [tasks]
    run = "go run ./cmd"
    build-image = "docker build ."
    ```
  </Step>

  <Step title="Test locally">
    ```bash theme={null}
    # Run the service
    mise run run

    # In another terminal, test it
    curl http://localhost:8080/health
    ```
  </Step>
</Steps>

## Create ECR Repository

Add the new repository to the ECR configuration:

<Steps>
  <Step title="Add repository to the list">
    Edit `terraform/live/shared/<REGION>/ecr-repositories/main.tf` and add your service to the `local.repos` list:

    ```hcl theme={null}
    locals {
      region = var.aws_region
      repos = [
        "services/container-only-dummy",
        "services/go-backend",
        "services/go-backend/migrations",
        "services/go-backend-no-migrations",
        "services/my-new-service"  # Add this
      ]
    }
    ```
  </Step>

  <Step title="Apply the Terraform change">
    Commit and open a PR. The Terramate workflow will run a preview, and once merged, the ECR repository will be created.

    Alternatively, apply directly:

    ```bash theme={null}
    cd terraform
    terramate run --tags ecr -- terraform apply
    ```
  </Step>
</Steps>

## Create Kubernetes Manifests

Choose your preferred templating approach. The kit demonstrates three options.

<Tabs>
  <Tab title="Helm (Recommended)">
    Create a Helm chart in `kubernetes/src/services/my-new-service/`:

    ```bash theme={null}
    mkdir -p kubernetes/src/services/my-new-service/templates
    ```

    **Chart.yaml:**

    ```yaml theme={null}
    apiVersion: v2
    name: my-new-service
    version: 0.1.0
    ```

    **values.yaml:**

    ```yaml theme={null}
    replicas: 1
    image:
      # Replace with your AWS account ID and region
      repository: "<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/services/my-new-service"
      version: 0.1.0 # staging_services/my-new-service
    ingress:
      hostname: "my-new-service.staging.<YOUR_DOMAIN>"
    ```

    **templates/deployment.yaml:**

    ```yaml theme={null}
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-new-service
      namespace: my-new-service
    spec:
      replicas: {{ .Values.replicas }}
      selector:
        matchLabels:
          app: my-new-service
      template:
        metadata:
          labels:
            app: my-new-service
        spec:
          containers:
            - name: my-new-service
              image: "{{ .Values.image.repository }}:{{ .Values.image.version }}"
              ports:
                - containerPort: 8080
              livenessProbe:
                httpGet:
                  path: /health
                  port: 8080
    ```

    Add additional templates for Service, Ingress, etc.
  </Tab>

  <Tab title="Kustomize">
    Create a Kustomize structure in `kubernetes/src/services/my-new-service/`:

    ```bash theme={null}
    mkdir -p kubernetes/src/services/my-new-service/{base,staging,production}
    ```

    **base/kustomization.yaml:**

    ```yaml theme={null}
    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    resources:
      - deployment.yaml
      - service.yaml
      - ingress.yaml
    ```

    **staging/kustomization.yaml:**

    ```yaml theme={null}
    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    resources:
      - ../base
    images:
      - name: my-new-service
        # Replace with your AWS account ID and region
        newName: "<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/services/my-new-service"
        newTag: "0.1.0"  # staging_services/my-new-service
    ```
  </Tab>
</Tabs>

## Add mise Tasks for Rendering

Create `kubernetes/src/services/my-new-service/mise.toml`:

```toml theme={null}
[tasks.render-cluster]
description = "Render manifests for a specific cluster"
usage = "arg '<cluster>' help='Target cluster (staging, production)'"
run = '''
#!/usr/bin/env bash
set -euo pipefail

CLUSTER="${usage_cluster}"
OUTPUT_DIR="$(git rev-parse --show-toplevel)/kubernetes/rendered/${CLUSTER}/services/my-new-service"

mise run //tools:render:prep-output-dir "$OUTPUT_DIR"

helm template my-new-service . \
  --namespace my-new-service \
  --values values.yaml \
  --values "values.${CLUSTER}.yaml" \
  | mise run //tools:render:split-k8s-docs "$OUTPUT_DIR"
'''
```

## Register with ArgoCD

Add the service to the ArgoCD services app-of-apps:

<Steps>
  <Step title="Add to services values">
    Edit `kubernetes/src/argocd/services/values.yaml`:

    ```yaml theme={null}
    applications:
      go-backend:
        enabled: true
      go-backend-helm:
        enabled: true
      my-new-service:
        enabled: true  # Add this
    ```
  </Step>

  <Step title="Render ArgoCD manifests">
    ```bash theme={null}
    mise run //kubernetes/src/argocd:render-all "<CLUSTER>" # Adds the ArgoCD Application to the app-of-apps
    mise run //kubernetes/src/services:render-all "<CLUSTER>" # Renders the service manifests
    ```
  </Step>
</Steps>

## Add to CI/CD

<Steps>
  <Step title="Add path filter">
    Edit `.github/utils/file-filters.yaml`:

    ```yaml theme={null}
    services/my-new-service:
      - 'services/my-new-service/**'
    ```
  </Step>

  <Step title="Add to workflow inputs">
    Edit `.github/workflows/ci-build-push.yml`:

    ```yaml theme={null}
    inputs:
      service:
        options:
          - services/go-backend
          - services/my-new-service  # Add this
    ```

    Also update `.github/workflows/gitops-update-manifests.yml`.
  </Step>
</Steps>

## Test Locally

<Steps>
  <Step title="Add to Tilt">
    Edit `kubernetes/src/services/Tiltfile` to include your new service:

    ```python theme={null}
    # Add your service
    load_dynamic('./my-new-service/Tiltfile')
    ```

    Create `kubernetes/src/services/my-new-service/Tiltfile` following the pattern of existing services.
  </Step>

  <Step title="Start Tilt">
    ```bash theme={null}
    cd local
    mise run tilt-up
    ```
  </Step>

  <Step title="Verify the service">
    Access via the sslip.io URL or port-forward:

    ```bash theme={null}
    kubectl port-forward svc/my-new-service -n my-new-service 8080:80
    curl http://localhost:8080/health
    ```
  </Step>
</Steps>

## Deploy to Staging

<Steps>
  <Step title="Commit and push">
    ```bash theme={null}
    git add .
    git commit -m "feat: add my-new-service"
    git push origin main
    ```
  </Step>

  <Step title="Monitor the deployment">
    1. Watch the CI workflow build the image
    2. Watch the GitOps workflow update manifests
    3. Check ArgoCD for the new Application
  </Step>
</Steps>

## Next Steps

* [Managing Secrets](/usage/operations/06-managing-secrets) - Add secrets for your service
* [Database Operations](/usage/operations/07-database-operations) - Add a database if needed
* [Observability](/usage/operations/08-observability) - Configure metrics and tracing
