π Manual Deploy β cloud:deploy
The manual path deploys straight from your machine to a cluster β no CI/CD, no git push. It's the fastest way to ship while you're iterating, learning, or running solo.
Prefer automated deploys on every push? Use the GitHub Actions path instead. Not sure? Start at The Deployment Journey.
When to use itβ
- β Solo developer or a quick test deploy.
- β A single-node VPS where side-loading the image over SSH is simplest.
- β You want to deploy right now without setting up secrets and workflows.
For team workflows and repeatable production releases, prefer CI/CD.
Prerequisitesβ
- A provisioned cluster (see The Deployment Journey) with its
larakube-<ip>context on your machine. - A project (
larakube new/larakube init). - A web host for the environment (the command prompts for one if it's missing).
Step 1 β (optional) choose a registryβ
On a single-node VPS, you can skip this β cloud:deploy will side-load the image straight into the node's k3s over SSH, no registry needed.
On a multi-node cluster (e.g. DOKS), every node must be able to pull the image, so configure a registry:
larakube cloud:configure:registry # pick GHCR or Docker Hub
Step 2 β deployβ
larakube cloud:deploy production
What it does, in order:
- Builds the production image for the node's architecture (
linux/amd64). - Ships the image β side-loads over SSH (no registry) or pushes to your configured registry.
- Grants a namespace-scoped credential β using your local admin context, it creates a
deployerServiceAccount + Role + RoleBinding locked to this app's{app}-{env}namespace. - Applies your manifests (ConfigMap/Secret from
.env.{env}, then the overlay) through that scoped credential β so the deploy itself runs with least privilege. - Waits for the rollout to finish.
Re-deploying is just running the command again.
π What "namespace-scoped" meansβ
Your admin kubeconfig never leaves your machine. The actual apply runs as the deployer ServiceAccount, which can only touch its own namespace. You can prove it after a deploy:
# yes inside its own namespaceβ¦
kubectl --context larakube-<ip> auth can-i create deployments -n myapp-production \
--as=system:serviceaccount:myapp-production:deployer # β yes
# β¦no anywhere else
kubectl --context larakube-<ip> auth can-i get secrets -n default \
--as=system:serviceaccount:myapp-production:deployer # β no
This is the same credential model the CI path will hand to GitHub β see Surgical Credentials.
Verifyβ
kubectl --context larakube-<ip> get pods -n myapp-production
kubectl --context larakube-<ip> logs -n myapp-production deployment/web -f
Then open your web host in a browser.
Troubleshootingβ
kubectl apply failed under the scoped credential: ... forbiddenβ the deploy hit a permission the scoped Role doesn't grant. Re-run after./buildif you've upgraded the CLI; if it persists, it's a Role gap worth reporting.Context 'larakube-<ip>' is missing or unreachableβ re-runlarakube cloud:provision(or check the box is up / firewall allows6443).- Image won't pull on a multi-node cluster β you side-loaded instead of pushing; configure a registry (Step 1) so every node can pull.