Deploy two independent services in one pipeline run using parallel stages that converge at the end.
┌─── Deploy Service A (cart-frontend) ───┐
Pipeline ───┤ ├──► Converge
└─── Deploy Service B (cart-backend) ───┘
(both run at the same time) (runs only after both succeed)
- Fan-out — Harness starts both Deploy stages at the same time (parallel block).
- Each stage deploys its own service into the shared Kubernetes namespace independently.
- Fan-in — The
Convergestage starts only after both deploys succeed. If either fails, it rolls back that service and the pipeline stops.
The shared infrastructure needs
allowSimultaneousDeployments: true— without it, Harness serializes parallel stages on the same infra, and they run one after another instead of together.
| What you need | Default in this repo |
|---|---|
| Kubernetes Cluster connector (with delegate) | account.k8s_delegate |
| DockerHub connector (anonymous OK) | account.Docker |
| GitHub connector pointing to this repo | account.GitHub |
| Harness org + project | default / project_1 |
.harness/
pipeline.yaml ← the pipeline (fan-out → fan-in)
serviceA.yaml ← Service A definition (cart-frontend, nginx:1.27-alpine)
serviceB.yaml ← Service B definition (cart-backend, nginx:stable-alpine)
environment.yaml ← shared Dev environment
infrastructure.yaml ← shared K8s infra (allowSimultaneousDeployments: true)
k8s/
serviceA/ ← Go-templated K8s manifests for Service A
namespace.yaml
deployment.yaml
service.yaml
values.yaml
serviceB/ ← Go-templated K8s manifests for Service B
namespace.yaml
deployment.yaml
service.yaml
values.yaml
The services pull manifests directly from this repo via a GitHub connector. No separate upload step needed.
Search all .harness/*.yaml files for # REPLACE: and swap in your actual values:
orgIdentifier— your Harness orgprojectIdentifier— your Harness projectconnectorRef: account.GitHub— your GitHub connector (pointing to this repo)connectorRef: account.k8s_delegate— your K8s connectorconnectorRef: account.Docker— your DockerHub connectorbranch: main— your default branch name- Namespace
tidbit-0068— whatever namespace you want
- Services —
Deployments → Services → New Service → YAML— importserviceA.yaml, thenserviceB.yaml - Environment —
Deployments → Environments → New → YAML— importenvironment.yaml - Infrastructure — open
env_dev_tid0068 → Infrastructure → New → YAML— importinfrastructure.yaml - Pipeline —
Pipelines → Create → Inline → YAML— pastepipeline.yaml
Hit Run on the pipeline. Both Deploy stages should start together and finish independently. Converge runs last.
kubectl -n tidbit-0068 get deployments
# Expected:
# cart-frontend-deployment 1/1 Ready
# cart-backend-deployment 1/1 ReadyThe Converge stage logs will show:
Service A stage status: SUCCESS
Service B stage status: SUCCESS
Stages run one after another, not in parallel
The infra is missing allowSimultaneousDeployments: true. Harness adds a resource-constraint step to every deploy stage — without this flag, two stages on the same infra are serialized.
namespace "tidbit-0068" not found
namespace.yaml must be listed first in each service's paths[] array. Also confirm createNamespace: true in values.yaml.
One pod overwrites the other
Each service must have a unique name in its values.yaml (cart-frontend vs cart-backend). This drives distinct Deployment names and app: label selectors.
Converge runs even though a deploy failed
Check the when condition on the Converge stage — it should be stageStatus: Success (the default). If changed, restore it.