> ## Documentation Index
> Fetch the complete documentation index at: https://docs.erp.arukz.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ARUKZ ERP on Kubernetes (k3s)

> The k3s setup in the repository: image, Kustomize manifests, Terraform for one Hetzner server and deploy-k8s.sh. Checked on a local k3d cluster, not live yet.

The repository holds a complete single-node Kubernetes setup for ARUKZ ERP: an application image (`docker/k8s/`), Kustomize manifests (`infra/k8s/`), Terraform that builds one Hetzner Cloud server (`infra/terraform/`) and a deploy script (`deploy-k8s.sh`). It has been run end to end on a local k3d cluster: fresh install, a second deploy on the same data, the worker taking queued jobs and the backup job writing a dump. It is **not live on any server yet**; the hosted service at erp.arukz.com still runs on Docker.

## What runs

| Part | Choice |
| - | - |
| Kubernetes | k3s, one node, with its built-in Traefik ingress |
| Pods | `web` (nginx and PHP-FPM), `worker` (`queue:work`), `scheduler` (`schedule:work`), `mysql` (MySQL 8.0 StatefulSet) |
| Image | `docker/k8s/Dockerfile`: the repository's own code, `composer install --no-dev`, built assets; MySQL is never started in it |
| Storage | `local-path` volumes for MySQL data and Laravel `storage/`, shared by web, worker and scheduler on the one node |
| Settings | A Kubernetes Secret made from your `.env` by the deploy script; never committed |
| Migrations | A release Job (`docker/k8s/release.sh`) runs before new web pods start: the full install on an empty database, otherwise new migrations, module installs, permissions and starter roles |
| Backups | A nightly CronJob writes a `mysqldump` and a `storage/` archive to the data volume and keeps 14 days |
| TLS | cert-manager with Let's Encrypt (Hetzner overlay only) |
| Server | Terraform: a Hetzner Cloud server (CPX31 by default, location `sin`), a firewall (80 and 443 open; 22 and 6443 only from your address), a 40 GB data volume |

## Try it on your own machine

You need Docker, [k3d](https://k3d.io), `kubectl` and a Laravel `.env` for the cluster.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
make k8s-local ENV_FILE=path/to/.env     # builds the image, creates the k3d cluster 'arukz', deploys
# open http://erp.localhost:8088
make k8s-local-down                      # deletes the cluster
```

On an empty database the release Job needs `ERP_ADMIN_NAME`, `ERP_ADMIN_EMAIL` and `ERP_ADMIN_PASSWORD` in the `.env`; a restored dump does not. The app answers only its trusted host names, and the web pods' health probes send a fixed one (`erp.localhost` on k3d), so `APP_TRUSTED_HOSTS` must list it as well as every name the cluster serves.

## Build a server on Hetzner

**1. Set your host names**

`infra/k8s/overlays/hetzner/kustomization.yaml` names the Ingress host and the host the health probes send; ARUKZ's values are its own domains. Change both to yours.

**2. Fill in the Terraform variables**

Copy `infra/terraform/terraform.tfvars.example` to `terraform.tfvars` (git-ignored) and set `hcloud_token`, `ssh_public_key` and `admin_cidrs`; optionally `location` and `server_type`. Terraform state stays on your machine.

**3. Create the server**

`make tf-plan`, then `make tf-apply`. cloud-init installs k3s on the new server.

**4. Bootstrap the cluster**

`ACME_EMAIL=you@example.in make k8s-bootstrap` fetches the kubeconfig and installs cert-manager with a Let's Encrypt issuer. Point your domain's DNS at the server's address first.

**5. Deploy**

`make k8s-hetzner ENV_FILE=path/to/.env`. The script refuses to run with uncommitted changes, builds the image, copies it to the server (no registry needed), runs the release Job, rolls the Deployments and smoke-tests the site.

`deploy-k8s.sh` can be run again safely. `kubectl -n arukz-erp rollout undo deploy/web` goes back one image; migrations are not undone, so restore a backup if one must be.

## Not in this setup

Several nodes, a managed database, autoscaling, an image registry, GitOps and monitoring beyond the app's own health check are not part of it. The image leaves out Faker, so the sample factory does not load; see [Self-hosting with Docker](https://docs.erp.arukz.com/developers/self-hosting-docker.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.