tezraJOURNAL
← All articles

Story 02 / Development · CI/CD · AI agents

GitLab, OrbStack & AI.
From edit to release.

Self-hosted GitLab, server workers and OrbStack on a Mac create short feedback loops. Here is how the setup can speed up development and help AI agents such as Codex carry tasks through to a working result.

Vitaliy Mishin16 min read
From an edit to a verified releaseTZ / 02
01 / LOCALMac + OrbStackCode · run · debug
02 / CIGitLab + workersCheck · build · artifact
03 / RELEASEVerify + releaseVersion · journey · rollback
Codex / AISkills connect the steps: context → action → check
A proposed workflow. Server resources run heavy jobs; the agent uses tools and verifies the result.

Development speed comes from short feedback loops

The time between a code change and a working release is spent on more than programming. You need to start an environment, wait for checks, produce a build, move it to a test environment and confirm that the intended version is running. When each transition needs manual work, even a small task can take a long time.

I favour a setup in which the Mac remains the workstation while a home server handles shared infrastructure and lengthy computation. The laptop runs the editor, browser and OrbStack. The server side provides a self-hosted GitLab instance and compute capacity for CI jobs. A clear path connects them: change, check, artifact, release.

This article describes that approach without exposing addresses, project names or the details of a particular network. It is an architecture and a set of practical recommendations, not a performance benchmark. The benefit depends on the application, hardware and where time is currently being lost.

A fast process provides early feedback and keeps the tested code connected to the version that gets released.

Three roles: the Mac, GitLab and workers

Separating responsibilities keeps the laptop from becoming the only place where anything can happen. Editing belongs close to the editor. History, review and check results belong in GitLab. Resource-intensive jobs belong on suitable workers.

Where each part of the work belongs
ComponentResponsibilityBenefit
Mac + OrbStackRun the application locally, check a change and debug it.A short path from an edit to a result in the browser.
Self-hosted GitLabRepository, merge requests, pipeline definitions and release history.One place to see which code was checked and what happened.
CI workersTests, builds, packaging and the checks required by the process.Lengthy jobs run independently of the workstation.
Release environmentRun a selected artifact and verify the result.A separate, observable action after a successful build.

Here, a worker means the compute environment executing a CI job. GitLab Runner receives jobs and runs them through an executor, for example in a container or Kubernetes Pod. The Runner, its underlying compute host and an application's background queue worker are different things. How GitLab Runner works.

Hosting GitLab yourself gives you control over its environment and operating rules. Putting it in your home does not automatically make pipelines faster: available capacity, storage, caching and queue length matter. Backups and maintenance also become your responsibility.

OrbStack on the Mac: the environment beside the code

OrbStack runs Docker containers and Docker Compose projects on macOS. It provides a convenient place for an application's backend, database, queue and other dependencies without installing the entire server stack directly into the operating system. Docker containers in OrbStack.

For a Kubernetes-based project, its built-in single-node cluster supports local checks of manifests and service interactions. It shares the container engine, so a built image can be used by a Pod without first pushing it to a registry. The image tag and pull policy still need to ensure that the intended build runs. Kubernetes in OrbStack.

The most useful improvement is often a consistent set of project commands: start the environment, apply migrations, run selected tests, inspect logs and stop services. Returning to a task then means following the same sequence. Stopping services should be separate from deleting data; finishing work is no reason to recreate the local database.

For the short feedback loop, I would run only what the change needs. An interface change needs the frontend and relevant APIs. A calculation change needs its module and tests. The full required checks remain in CI; the local loop catches an obvious mistake before the change is submitted.

Containers bring environments closer together without making them identical. Dependency versions, database extensions, time settings, file permissions and application configuration still need explicit definitions. A small anonymised dataset that reproduces the relevant cases is preferable to copying working data.

Which jobs belong on home workers?

Good candidates include full test suites, frontend builds, dependency compilation, container packaging and integration checks. Once a change is pushed to GitLab, you can continue working while the server handles them. Freeing the workstation is useful even when the job itself takes the same amount of time.

Several workers can execute independent jobs concurrently. But several Pods on one machine share its CPU, memory and disk. Excessive concurrency can turn an intended speedup into resource contention. Start with a modest number of simultaneous jobs and observe both utilisation and queue time.

If the home server also stores documents, handles email or runs a media library, builds should not crowd out those services. Set appropriate resource limits and allow sufficient disk space. Workers do not have to share GitLab's physical host: a separate compute node can be the next step as demand grows.

Independent tests can run in parallel, while needs expresses dependencies so a ready job does not wait for unrelated work. Those dependencies must still represent the actual release conditions. Bypassing a required check is not a valid optimisation. GitLab job dependencies.

Apple Silicon and the server: the same code, different architectures

A local environment on an Apple Silicon Mac commonly runs ARM64, while the server may use AMD64. The distinction matters for native libraries, packages with binary modules and images available for only one platform. A successful laptop run does not by itself establish server compatibility.

Docker supports multi-platform builds. Emulation can run and build images for another architecture, but heavy compilation under emulation can be slower. A native worker for the target architecture is useful for that work. Multi-platform build strategies.

A practical agreement is to use the convenient local architecture for rapid iteration, then build and test the server's target variant before release. If the product ships on both platforms, both belong in the test matrix. Testing one does not replace testing the other.

A cache saves work; an artifact records the result

These two concepts are easy to confuse. A cache reuses downloaded dependencies and other intermediate data. Its key should account for compatibility factors such as the lock file, platform and toolchain. A job must remain correct when the cache is empty. GitLab CI/CD caching.

An artifact is a saved result of a particular job: a frontend archive, test report or package. It can be passed to subsequent jobs and associated with a pipeline. A container image is normally published to a registry, with its digest recorded for release. Job artifacts · Container Registry.

This leads to a useful rule: release the output that passed the required checks. Rebuilding the application on a laptop or server after testing produces a different build. Even with the same source code, dependencies or tools may differ.

A release should therefore have at least a commit identifier, a successful pipeline reference and an artifact identifier. For a container, record the digest; for an archive, its checksum. Together, they answer the simple question: “What is actually running?”

One change, from editor to release

  1. Check locally. Run the necessary application components in OrbStack. Reproduce the issue, change the code and execute suitable tests.
  2. Submit the change for review. A commit and merge request provide a fixed version for discussion. CI runs checks against that version.
  3. Prepare a release candidate. Build an artifact for the commit that will actually be released. Run the checks relevant to the built output. If a merge changes the source tree, the new commit needs its own pipeline.
  4. Deploy to a test environment. Start the chosen artifact with that environment's configuration. Check migrations, service readiness and the main user journey.
  5. Release the same version. Once accepted, promote the same artifact to the working environment, provided its format allows environment configuration to be separated from the build.
  6. Verify and keep a way back. Confirm the version and behaviour, then record the outcome. Keep the previous artifact available for rollback.

Frontend configuration is sometimes embedded at build time. In that case, you cannot promise to promote an identical archive between environments unchanged. Either move configuration to runtime or validate each separately built artifact. Reproducibility has to match how the application works.

Two pipelines should not modify the same environment simultaneously. GitLab's resource_group can serialize jobs. Release ordering and preventing stale candidates need separate configuration: a lock alone does not guarantee that the intended version runs last. Resource groups.

Rolling back application code and rolling back data are separate operations. Restoring an older image does not undo a database migration. Before release, understand whether the previous application version remains compatible with the schema and have an appropriate backup. A fast release needs a clear recovery path.

How this infrastructure makes AI agents more useful

An agent such as Codex is more useful when it can test its solution in a running project. It needs source code, a clear startup procedure, tests and access to CI results. The home server adds compute capacity; OrbStack provides a place for quick local checks. The potential gain is across the journey from task to result: fewer manual handoffs, guesses about the environment and repeated attempts.

Suppose an API calculation needs a fix. The agent finds the relevant code, reproduces the issue in OrbStack, changes the implementation and runs an appropriate test. After the change is submitted, GitLab checks the fixed commit on server workers. The agent reads that specific pipeline's result, diagnoses a failure or prepares information about the verified artifact. Deployment is a separate action with a defined destination and version.

THE AGENT'S WORK CYCLE
TaskContext & skillActionCheckNext step
A failed check leads back to diagnosis. Completion requires an observable result: a test, an artifact version or a successful user journey.

The agent chooses the next step, project commands carry out operations, and CI independently checks the result. For example, an environment inspection command can return its type, current version and service status as structured data. That is more useful than a long stream of output the agent must interpret from scratch on every run.

Multiple agents need separate working copies or worktrees, agreed areas of change and enough resources. A CI worker and an AI agent have different roles: another worker adds capacity for checks but does not itself create another agent. Concurrent work helps when the tasks are genuinely independent.

Where commands run and where the model runs are also separate questions. A local terminal and a home GitLab instance do not mean that a cloud model runs on the home server. The source code and logs sent to the model depend on the agent's connection and configuration; those materials should exclude secrets.

Which skills to build for development and releases

A skill packages instructions for a recurring task. In Codex it contains a SKILL.md with a name, description and workflow, optionally accompanied by references and scripts. The agent initially sees a short description and reads the full instructions when it selects the skill. Selection can be explicit or based on the task. Skills documentation.

Keep general repository guidance in AGENTS.md: where to find commands, which checks apply to a change and which environment boundaries matter. Put a particular operation's procedure in a separate skill. A deployment procedure then need not crowd an ordinary copy edit. How Codex reads AGENTS.md.

A proposed skill set for this infrastructure
SkillWhen to use it and what it doesVerifiable result
project-contextStarting an unfamiliar task: identify the repository, branch, existing changes, relevant services and environment boundaries.A concise task map and verification commands, with existing work preserved.
local-devRunning and debugging in OrbStack: select an explicit local context, start dependencies, rebuild the relevant service and run tests.A working local application, check results and relevant error logs.
ci-diagnosticsInvestigating failed CI: find the pipeline for the intended commit, read relevant logs and distinguish a code defect from an unavailable worker.The cause, a justified fix or a precise infrastructure diagnosis.
release-candidatePreparing a release: connect the commit, successful checks and artifact; identify configuration changes and migrations.A candidate manifest with version, pipeline, digest or checksum, and release conditions.
deploy-and-verifyCarrying out an authorised release: confirm the target, apply the selected artifact, check readiness and the main user journey.A confirmed version in the target environment and evidence of its behaviour.
rollbackRecovering a failed release: identify the previous version and data compatibility, then follow the authorised recovery procedure.A restored and rechecked environment, or a clear reason to stop.

This is a proposed set of skills, not a claim that they are already installed. Start with local-dev and ci-diagnostics: they remove repeated work without requiring the entire release process to be automated at once.

Access should use a shared mechanism: an environment registry without secret values and a separate credential store, such as Keychain on the Mac. The skill explains how to request the appropriate access; it does not embed credentials. CLI, API or MCP tools make actions available, while the skill explains when and how to use them. How skills complement MCP tools.

Loading a skill neither retrains the model nor grants additional permissions. Tools still need their own controls: environment checks, permitted paths, scoped credentials and refusal to perform sensitive operations without the required inputs. A Markdown file cannot enforce those boundaries by itself.

Designing a skill that can handle routine work

A useful instruction defines its trigger, inputs, sequence and completion criteria. Below is a shortened SKILL.md sketch for local verification. The commands illustrate a proposed project interface; they must be implemented and checked before this example can be used.

---
name: local-dev
description: Run and check a project locally in OrbStack.
---

Scope: the project's local environment only.
Inputs: working copy, affected service, verification scenario.

1. Read the AGENTS.md guidance relevant to the task.
2. Inspect the working copy and existing changes.
3. Confirm the local environment with bin/local status.
4. Run bin/local up and the relevant bin/local test.
5. On failure, collect relevant logs and diagnose the cause.
6. Check the user journey when the change affects it.

Stop if the environment is remote or cannot be identified.
Preserve data when stopping services normally.
Report: change, environment, checks, result, limitations.

The actual bin/local implementation should select the permitted context and namespace, validate its arguments and fail when the environment does not match. A script may live in the repository; a shared helper may accompany the skill. Encode repeatable checks in software, leaving workflow decisions and unusual cases to the agent.

Exercise the boundaries with several cases: normal startup, a failing test, an unavailable dependency, missing access, someone else's uncommitted changes and a selected remote environment. In the last case, the local command should refuse to proceed. These cases reveal more than one successful run.

A release skill also needs an exact destination, artifact version, permitted scope of changes and recovery conditions. Already authorised work need not become a series of identical confirmations. If the request leaves the environment unclear or would require unapproved data deletion, resolve that uncertainty before acting.

The agent's final report should be short and verifiable: what changed, which commit was checked, which tests ran, what was deployed and what remains unverified. A successful build establishes that the build passed; a successful release additionally requires checking the application in its target environment.

How to tell whether the process is actually faster

Measure several small intervals rather than only total pipeline duration. That reveals whether another worker would help or whether the delay is elsewhere.

What to measure before and after a change
IntervalWhat it revealsPossible action
Edit → local checkEveryday feedback speed.Simplify startup and check the relevant application component.
Job created → job startedWaiting for an available worker.Inspect the queue and available capacity.
Job started → artifact readyThe cost of tests and builds.Review caching, dependencies and independent jobs.
Candidate → verified releaseManual transitions and deployment complexity.Automate promotion and version verification.
Agent task → verified resultRepeated attempts, access delays and manual guidance.Clarify the skill, project commands and result format.

Compare similar changes and distinguish cold from warm caches. Alongside elapsed time, track failed releases and recovery time. Otherwise, it is easy to make a process look faster by removing checks and leaving users to discover the errors.

Boundaries that keep the setup useful

Local commands should unambiguously select the local environment. A release procedure should separately select its destination. The context, namespace and configuration must not depend on incidental terminal state. Development and release credentials should be separate and limited to the permissions they need.

Workers execute code from repositories. An experimental branch should not automatically receive access to working data, release credentials or unrestricted host control. Job isolation and limited privileges are part of CI design, particularly on a machine shared with other home services. GitLab Runner security.

Physical constraints remain: power, connectivity, backups and disk capacity. If the server becomes unavailable, development in an already prepared local environment can continue while shared checks and releases wait. If minimal maintenance is the priority, hosted GitLab and managed runners may be a better fit.

Grow the setup gradually: reproducible startup in OrbStack, one CI worker, a saved artifact, a separate deployment step and verification. Package recurring operations as skills; add caching and concurrency where measurements show waiting. That is how a home server can help both a developer and an AI agent carry a change through to a verified release.

For other uses of the platform, see Single-node Kubernetes as a smart-home hub.

Vitaliy Mishin
Vitaliy Mishin

Principal Engineer & Systems Architect at Tezra

System architecture, secure access and infrastructure operations.