Unified Development and Deployment: The Compelling Combination of Monorepo and GitOps
Key point
By combining Monorepo and GitOps, the team unified everything from FE development to deployment on a container basis.
Details
The Saramin FE development team was preparing to separate the FE for its main service while gradually improving legacy systems, and chose Monorepo as the solution. Ahead of the first service deployment, the team also redesigned the deployment approach and introduced GitOps.
The existing pipeline worked as follows: check changes on a review server from the feature branch, deploy to the dev server upon merging into develop, and after final QA, deploy to production via main. Production deployment required sequentially taking down multiple web servers, performing build and deploy, and then manually handling pm2 reload and LB recovery.
The problem was that, without a dedicated DevOps organization, FE developers had to configure the environment themselves. The setup deployed artifacts produced by next build, started them with next start, and proxied them through nginx. However, since feature and hotfix branches changed frequently, it was difficult to flexibly match web servers to branches. In the end, about 3 review servers had to be operated separately, leading to the conclusion that a more flexible container-based deployment environment was needed.
Image build
First, a Dockerfile was placed under /apps, structured so it could be reused for future services with a similar stack. The base image was node:20-alpine, with libc6-compat and corepack enable applied to enable the use of pnpm and turbo.
The build stage is split into Multi Stage.
- builder stage: Copies the entire source, then uses
turbo prune --scope=${APP_NAME} --dockerto extract only the necessary packages. - installer stage: Based on the prune results, runs
turbo run build:${APP_ENV} --filter=${APP_NAME}to build. - runner stage: Creates a non-root user (
nextjs) and copies only.next/standalone, static, and public to minimize the runtime image.
CI builds were done with kaniko. It allows building images inside a container without a Docker Daemon, and doesn't require Host root privileges, making it more suitable. The internal Harbor was used as the image registry, and registry credentials were managed as GitLab variables.
A retention policy was also configured. /web1/develop keeps the 10 most recently pushed images, while /web1/feature* retains only the most recent 5 images pushed within the last 7 days.
Chart override
Helm chart was chosen to manage deployment resources declaratively. Between kustomize and Helm chart, the deciding factor was the ability to flexibly respond with per-environment and per-service values files.
The chart is composed of values.yaml, deployment.yaml, service.yaml, ingress.yaml, hpa.yaml, secrets.yaml, and others. Default resources were set to CPU 100m and memory 128Mi, and nodeAffinity was used to ensure deployment only to FE-dedicated nodes (sri-fe). For health checks, both livenessProbe and readinessProbe used tcpSocket on port 3000.
In the pipeline, values from the sample/ folder are copied, then the following items are automatically modified using yq:
nameOverride,fullnameOverrideimage.repository,image.tagimagePullSecrets,secretsNameimageCredentials.*ingress.hosts[0].host
The modified values are then committed and pushed back to the chart repo. This approach directly updates the file that ArgoCD watches, triggering the deployment.
App create & update
Finally, the ArgoCD Application was declaratively created and updated. Instead of managing it manually via the Dashboard, the argocd app create CLI was used to create the app based on application.yaml, and --upsert handled updates as well.
By chaining together Monorepo, kaniko, Helm chart, and ArgoCD in this way, the team unified everything from FE service development to image build, deployment configuration, and cluster application into a single Git flow. As a result, they built a structure that enables both flexible branch-based deployment and operational automation at the same time.
This summary was generated automatically by AI. Check the original for the author's claims and context. Copyright belongs to the original author.
Our guide explains how the AI works. Report summary errors, attribution issues, or removal requests via Contact.