Infrastructure · Ongoing
Shipyard
A self-hosted control plane for the servers and Laravel applications I run myself.
Shipyard started with a mundane frustration: I knew how to provision and deploy a server, but that knowledge was distributed across terminal history, scripts, and habits. Repeating it was slow; reviewing it later was harder; recovering from a partial failure depended too much on memory.
This is not a hosting product for other people. It is the operating system I wanted for the small number of VPS instances and Laravel applications I am responsible for.
The problem I wanted to remove
The job was never just “run this command”. A new server needs a predictable stack, a site needs a safe deployment path, and an incident needs enough history to answer what changed. The usual escape hatch is SSH. It is useful, but it should be the exception when the work is known and repeatable.
Shipyard turns those known operations into a deliberate flow: prepare a server, create a site, connect the repository, deploy an immutable release, and inspect the result.
The product bet
I deliberately kept the scope narrow. Shipyard is for one technical owner managing one or two Ubuntu VPS instances—not a multi-tenant platform, a Docker abstraction, or a commercial alternative to managed hosting.
That constraint drove a few rules:
- Prefer explicit operations over a clever layer that hides them.
- Validate early and stop before changing a production machine when a precondition fails.
- Keep provisioning idempotent, so retrying after a failure does not create a second, slightly different server.
- Record every remote operation with its command, output, duration, and final status.
How it evolved
The first useful slice was not a dashboard. It was a reliable path from the control plane to a server: credentials, SSH execution, queued work, and an execution history that made the remote boundary observable.
From there, the product expanded along the actual operational path:
- Provisioning turned a fresh Ubuntu VPS into a known Laravel-ready host, with the services the applications need.
- Sites and deployments added repository wiring, atomic releases, health checks, rollback support, SSL, queues, and schedulers.
- Reliability work made those operations safe to repeat and easier to diagnose instead of treating the happy path as the whole product.
The important design iteration was moving the craft out of shell snippets and into a system that can show its work. A deploy is not just “success” or “failure”; it has a release, a sequence of operations, output, elapsed time, and a recoverable next step.
How it is put together
Shipyard panel (Laravel + Inertia/React)
│
├── queue workers and execution history
└── SSH commands
│
▼
Managed Ubuntu VPS
nginx · PHP-FPM · PostgreSQL · Redis · Supervisor · Certbot
│
▼
Atomic release layout
releases/<version> → current
The panel runs on the same VPS it manages. The installer establishes the control-plane stack, creates the owner and the protected self-site, and leaves later application work following the same release conventions. That is intentionally less flexible than a general platform, but much easier for me to reason about.
What I still want to prove
The value is not that Shipyard can execute every server command. It is whether it makes the safe path the normal one: fewer routine SSH sessions, faster recovery when a deployment breaks, and enough operational context to trust a decision made days later.
I want to document that with real runs before making bigger claims.
Screens to add
- A server moving through preflight, provisioning, and verification.
- A deployment timeline with the release, health check, and output visible.
- A failure state and the information available before retrying or rolling back.