09 Aug 2026 · 5 min read
Under the hood of `govard env up`
A close look at the five-phase pipeline that brings a Govard environment online — from blueprint rendering and networking to resilient image pulls and global services.
When you run govard env up in a project directory, a lot happens before
your site is reachable at https://project.test. Govard is a Go-native
orchestrator, so the whole startup sequence runs as a single fast binary —
no PHP bootstrap script, no Node wrapper, no shell ceremony. This post walks
through exactly what that binary does, phase by phase, and what each step
buys you.
The high-level design is documented in the architecture overview, and every flag referenced here is described in the configuration reference.
The 5-phase startup pipeline (detect, validate, render, start, verify)
govard env up (alias govard up) is deliberately split into five ordered
phases. Each one has a clear contract, and a failure early stops the chain
before it wastes time pulling images or starting containers.
- Detect — Govard reads
.govard.yml(and any active profile layers) and identifies the framework in the current directory. Detection is registry-driven, so it never hard-codes a branch or a framework name. - Validate — the resolved configuration is checked for internal
consistency: required services exist, ports don't collide, and the
effective profile resolves cleanly.
govard doctorruns a superset of these checks on demand. - Render — the matching blueprint is expanded into concrete Docker Compose files, environment files, and virtual-host definitions.
- Start — compose is brought up, images are pulled (with local fallback), and networks are attached.
- Verify — Govard probes the running services, confirms the reverse
proxy answers on the
.testdomain, and reports a ready summary.
You can watch each phase in the logs:
govard env up
govard env logs # tail all service logs
govard env ps # show container status
If a phase fails, the partial environment is left in a known state so you
can inspect it with govard env ps rather than staring at a half-built
mess.
Blueprint rendering (Go text/template, framework-specific services.yml)
The "render" phase is where Govard earns its flexibility. Each framework
ships a blueprint — a set of Go text/template
assets — under its own package, while assets shared across frameworks live
in a common blueprints directory. This keeps framework logic isolated and
the shared contract in one place.
For a detected project, Govard renders a framework-specific services.yml
that declares the containers that project actually needs: the web server,
the PHP-FPM variant matching the detected version, the database, and any
optional services you enabled in configuration. Because the templates are
data-driven, the same Magento, Laravel, Symfony, or WordPress project gets
a tailored compose file without you maintaining one by hand.
The effective output is controlled by your
configuration,
including profile layers that merge over the base .govard.yml. You can
inspect what Govard resolved before starting anything, which is the fastest
way to debug a surprising service set:
govard config profile # show the resolved effective profile
govard env ps # confirm what actually came up
Adding support for a new framework means contributing a blueprint package and registering it, not forking the core — see adding a framework.
Networking & HTTPS (Caddy reverse proxy, dnsmasq *.test, 308 redirect, root CA auto-trust)
Local HTTPS is a first-class concern in Govard, not an afterthought. During
the start phase, a Caddy reverse proxy is wired in front of your project and
issued a certificate from Govard's local root CA. That root CA is
auto-trusted on your machine during setup, so browsers show a real green
lock for https://project.test with no manual例外.
Two pieces make the domain magic work:
- dnsmasq
*.test— a local resolver answers every*.testquery with127.0.0.1, so any project you bring up is immediately addressable without editing/etc/hosts. - 308 redirect — HTTP requests are permanently redirected (status
308) to HTTPS, so bookmarks and integrations always land on the secure origin.
The net effect: govard env up ends with a URL you can paste into a browser
and trust, with no curl -k workarounds. The proxy and DNS wiring are part
of the orchestration described in the
architecture overview.
Tip: if a
.testdomain stops resolving after a reboot, the dnsmasq service is usually the culprit —govard doctorflags it, and a freshgovard env upre-establishes the resolver.
Resilient image pull & local fallback
Image pulls are the part of local dev most likely to fail on a flaky connection, so Govard treats them defensively. When bringing a stack up, it attempts the normal registry pull, and if that fails (timeout, auth hiccup, offline), it falls back to any locally cached image rather than aborting the whole environment. You get a working, if slightly stale, container instead of a dead startup.
This matters for repeatable workflows: once you've pulled a PHP or database
image once, subsequent govard env up runs are resilient to network
trouble. When you do want to force fresh images, a normal govard env restart plus a re-pull keeps things current, and govard self-update
keeps the orchestrator itself current.
govard env down # stop and remove containers
govard self-update # update the Govard binary
govard env up # bring it back, reusing cached images
Global services (Mailpit, PHPMyAdmin, Portainer)
Some services are useful across every project, so Govard runs them once as shared global services rather than spinning up a copy per project. The standard set includes:
- Mailpit — captures outbound email from your app so you can inspect password resets and order confirmations in a local inbox instead of guessing whether mail "sent".
- PHPMyAdmin — a web UI for the database, handy when a raw
govard dbshell isn't enough. - Portainer — a management UI for the underlying Docker containers, useful when you want to see resource usage or restart a single container without leaving the browser.
These global services are brought up as part of the environment lifecycle and
exposed on their own local ports, keeping them out of your per-project
services.yml while still being one govard env up away. For database work
specifically, the govard db family of commands — govard db dump,
govard db import, govard db top — talks to the same database container
the global UIs visualize.
Next in this series: Framework intelligence & discovery in Govard —
/blog/govard-framework-discovery