| name | devctl |
| description | Guide for using the `devctl` CLI to manage the SalaryHero local Kubernetes development environment (minikube-based). Use this skill whenever someone asks about: starting or stopping services locally, bootstrapping the dev environment, rebuilding a service image, watching for code changes, streaming pod logs, shelling into a pod, checking deployed image tags, managing /etc/hosts entries, running database migrations locally, or syncing/cloning all SalaryHero GitHub repositories. Also use it when someone asks "how do I deploy X locally", "why is my service not updating", "how do I get the local env running", or "how do I clone all repos". Covers first-time setup through day-to-day workflows.
|
devctl — SalaryHero Local Dev Tool
devctl wraps minikube + kubectl + kustomize into one CLI for running the
SalaryHero stack locally. All services live in the salaryhero-local namespace
by default.
Critical env var
export DEVCTL_INFRA_DIR=/path/to/infra
Without this, kustomize overlays will not be found. Add it to your shell profile.
Service inventory
Run devctl services list --json for the canonical, always-current list of
services, their repos, ports, and kustomize paths — generated from
internal/services/registry.go, the single source of truth.
Current services: account-api, account-worker, auth-api, bank-api,
bank-worker, balance-worker, company-service, console, flexben-api,
flexben-worker
Command reference
bootstrap — full first-time setup
devctl bootstrap
devctl bootstrap account-api
devctl bootstrap --no-build
devctl bootstrap --no-hosts
devctl bootstrap --no-wait
devctl bootstrap --force
devctl bootstrap --namespace dev-alice
Bootstrap does: check minikube, create namespace, build images, deploy via
kustomize, write /etc/hosts entries, wait for pods ready. All three of
--build, --hosts, and --wait default to true; negate any with --no-<flag>.
start — deploy / redeploy
devctl start
devctl start account-api
devctl start account-api --build
devctl start --no-cache
devctl start account-api --tag main-a1b2c3d4
Image tag defaults to <branch>-<shortSHA> read from the service's own git
repo (e.g. ../account-api), not the infra repo.
When --build is used, a rollout restart is forced even if kustomize manifests
are unchanged — this is necessary when the pod is in ImagePullBackOff.
stop
devctl stop
devctl stop account-api bank-api
watch — file-watching auto-rebuild
devctl watch
devctl watch account-api
Detects changes in each service's WatchPaths, rebuilds the image, and
redeploys automatically.
logs
devctl logs account-api
devctl logs account-api -f
shell — exec into a pod
devctl shell account-api
devctl shell account-api -- ls /app
image verify — inspect deployed tags
devctl image verify
hosts — manage /etc/hosts
devctl hosts apply
devctl hosts show
devctl hosts plan
devctl hosts remove
services list — inspect the service registry
devctl services list
devctl services list --json
Outputs the canonical list from internal/services/registry.go. Use --json for
tooling, scripts, or AI context (e.g. devctl services list --json is the
authoritative service inventory).
migrate — database migrations
devctl migrate
Note: prefer sh-db-schema over devctl migrate for schema changes.
sync — clone or update all SalaryHero repos
devctl sync
devctl sync --path ~/projects
devctl sync --verbose
devctl sync --config ~/my.json
On first run creates ~/.config/devctl/sync.json with all known repos
pre-populated. Edit that file to add or remove repos.
Config file (~/.config/devctl/sync.json):
{
"org": "salaryhero",
"checkout_path": "~/Code/salaryhero",
"repos": ["account-api", "auth-api", "..."]
}
Update behaviour per repo:
- Runs
git fetch --all --prune first (always safe)
- If current branch has a remote upstream →
git pull --ff-only
- If no upstream (orphaned feature branch) → checks out
develop and pulls
- Always prints the current branch name so you know where each repo stands
First-time setup
minikube start
minikube addons enable ingress
bash scripts/setup-local-tls.sh install
export DEVCTL_INFRA_DIR=/path/to/infra
devctl bootstrap
After a cluster reset (cert already exists):
bash scripts/setup-local-tls.sh apply
devctl bootstrap
Daily workflow
minikube start
devctl start
devctl watch account-api
GitHub token (private npm packages)
Resolution order:
--gh-token flag
GH_TOKEN env var
GITHUB_TOKEN env var
gh auth token (if gh CLI is authenticated)
Namespace isolation
devctl bootstrap --namespace dev-alice
devctl watch --namespace dev-alice
devctl logs account-api --namespace dev-alice
All commands accept --namespace / -n.
SQS / LocalStack scripts
bash scripts/add-sqs-queues.sh recreate-all
bash scripts/add-sqs-queues.sh create my-queue
bash scripts/add-sqs-queues.sh create my-q my-q-dlq
bash scripts/add-sqs-queues.sh list
bash scripts/add-sqs-queues.sh messages my-q peek
bash scripts/add-sqs-queues.sh messages my-q purge
Common problems
| Symptom | Fix |
|---|
Pod stuck in ImagePullBackOff after devctl start | Use devctl start --build to force a rebuild and rollout restart |
| Kustomize paths wrong / "overlay not found" | Check DEVCTL_INFRA_DIR is set and points to the infra repo |
Browser shows CORS error on *.localdev.test | Self-signed cert rejected — run bash scripts/setup-local-tls.sh install and restart browser |
devctl hosts apply fails with permission error | Needs write access to /etc/hosts; run with sudo or grant user permission |
Image tag is dev instead of branch-SHA | devctl could not read git info from the service repo — ensure the service repo exists at ../service-name relative to infra |
account-worker not updating after account-api build | They share an image; rebuilding account-api also rebuilds the worker — start both |
sync clone fails with "Could not resolve to a Repository" | Repo name in ~/.config/devctl/sync.json doesn't match GitHub — fix the name and re-run |
sync pull fails with merge conflicts | devctl only fast-forwards; resolve the conflict manually then re-run devctl sync |
sync reports "could not checkout develop" | Repo has no develop branch (some use main) — check out the correct branch manually |
Building devctl itself
cd /path/to/sh-devctl
make build
make install-local
make install
make test