slopkit-push
Use when working on SlopKit Push deploys — deploy.nix, the GitLab catalog component slopkit/push/deploy, flake.nix push input, dockerfile vs static apps, app.components, extraDomains, k3s/kubenix/Traefik/cert-manager, local e2e VM, or Roast/Hurl/Clown/site production deploys.
Synopsis
The YAML description states when to use this skill. disable-model-invocation is unset.
Source file: skills/slopkit-push/SKILL.md.
Front matter
- name
slopkit-push- description
- Use when working on SlopKit Push deploys — deploy.nix, the GitLab catalog component slopkit/push/deploy, flake.nix push input, dockerfile vs static apps, app.components, extraDomains, k3s/kubenix/Traefik/cert-manager, local e2e VM, or Roast/Hurl/Clown/site production deploys.
- disable-model-invocation
- unset
Full contents
---
name: slopkit-push
description: Use when working on SlopKit Push deploys — deploy.nix, the GitLab catalog component slopkit/push/deploy, flake.nix push input, dockerfile vs static apps, app.components, extraDomains, k3s/kubenix/Traefik/cert-manager, local e2e VM, or Roast/Hurl/Clown/site production deploys.
---
# SlopKit Push
Nix + Kubenix + k3s. Apps declare `deploy.nix`; CI builds (dockerfile) or copies files (static) and applies manifests.
Repo: GitLab `slopkit/push` (local clone often `deployer`). Canonical options: `modules/app.nix`. Checkout wins if this skill drifts.
Public names (not repo/k8s names): **Clown** (`clown.slopkit.dev`, legacy `ui.slopkit.dev`, repo `slopkit/ui`), **Roast**, **Hurl** (`slopkit/hurl`). Hub: `slopkit.dev` (`slopkit/site`).
## Pipeline
`deploy.nix` → Kubenix YAML → kubectl onto k3s (omega or local VM). Dockerfile apps: podman build, then registry push or k3s import. Static apps: S3Bucket (`website = true`) on Garage, files uploaded with rclone; `static-nginx` keeps pinned `nginx:1.27.4-alpine` and a content PVC. Ingress: Traefik + cert-manager ClusterIssuer `default`. Static S3 Ingress lives in the `garage` namespace and targets the Garage website port.
Catalog job clones `slopkit/push@latest` into `.push` and runs `ci/deploy.sh`. Target (first match): host kubeconfig → `DEPLOY_HOST` + SSH key → `KUBECONFIG` / `DEPLOY_KUBECONFIG`.
## `deploy.nix`
`app.name` = namespace + prefix. In the Push monorepo it must match the flake package name.
| Field | Meaning |
| --- | --- |
| `type` | `"dockerfile"`, `"static"` (S3/Garage), or `"static-nginx"` (PVC+nginx) |
| `domain` | Hostname; `null` → `<name>.<rootDomain>` |
| `extraDomains` | Extra hostnames, same routes (static S3: redirect to primary) |
| `env` | Attrset → Secret. `{ variables, ... }:` + `variables.NAME` for GitLab CI/CD (empty discovered values fail) |
| `components` | Named processes; `{}` = one process |
| `sso.enable` | Authelia on non-`localOnly` routes |
| `postgres.enable` | Database CR, instance `postgres-main` |
| `storage.enable` | PVC `fast-essential`, default `/data` |
| `staticPaths` | Paths relative to `deploy.nix`, uploaded to S3 or copied onto the content PVC |
| `contentRevision` | static-nginx PVC suffix (CI: `CI_COMMIT_SHORT_SHA`) |
Components: `dockerfile`, `image`, `port` (80), `prefix` (`["/"]`), `domain`, `extraDomains`, `localOnly`, `env`, `storage`, `healthPath` (`null` = no probes), `privileged` (`false`; sets container `securityContext.privileged`). No `domain` → share `app.domain`, split by `prefix`.
```nix
{ variables, ... }: {
app = {
name = "slopkit-roast";
type = "dockerfile";
domain = "roast.slopkit.dev";
env.ADMIN_ENCRYPTION_KEY = variables.ADMIN_ENCRYPTION_KEY;
};
}
```
List only fields this app uses: `name`, `type`, `domain` when it is not derived, then `staticPaths`, `env`, `sso`, `postgres`, `storage`, `extraDomains`, or `components` when those apply.
Set `resources` or `replicas` when the user named CPU, memory, or replica count, or the process is documented as heavy or needs more than one replica. Defaults: CPU/memory omitted (`null`), `replicas = 1`. Another landing's `100m` / `128Mi` / `replicas = 1` is not a requirement.
Multi-process: `apps/example-web` (`frontend` `/` + `backend` `/api`). Static: `type = "static"; staticPaths = [ "index.html" ];`. Old nginx+PVC: `type = "static-nginx"`.
## Consumer repo
```nix
# flake.nix — input name `push` (CI evals the cloned Push flake; this is for local lib)
{
inputs.push.url = "git+https://gitlab.techtailors.net/slopkit/push?ref=latest";
inputs.nixpkgs.follows = "push/nixpkgs";
outputs = { push, ... }: { lib.x86_64-linux = push.lib.x86_64-linux; };
}
```
```yaml
# .gitlab-ci.yml — catalog only, never include: project:
include:
- component: $CI_SERVER_FQDN/slopkit/push/deploy@latest
inputs:
deploy_nix: deploy.nix
push_root_domain: slopkit.dev
deploy:
extends: .push:deploy
```
Always `@latest`. Allowlist the app on slopkit/push job tokens (or the `slopkit` group). `.push:shell` tags `[nix]`; `.push:deploy` tags `[nix, k8s]`. Inputs: `deploy_nix`, `push_root_domain`, `push_project_path`, `container_runtime` (podman), `container_platform` (`linux/amd64`).
Set group deploy token `REGISTRY_PULL_USER` / `REGISTRY_PULL_PASSWORD` (job tokens die with the pipeline). Always `CONTAINER_PLATFORM=linux/amd64` on the x86_64 VM.
Cancel running GitLab pipelines on the project before pushing CI fixes (shared `omega-nix` runner).
## Ingress / local URLs
`ingressClassName: traefik`. Prod TLS: ClusterIssuer **`default`**. Local nip.io: pin Traefik entrypoint **`web`**. Do **not** pin `websecure`-only in prod.
SSO (`app.sso.enable`): `*.slopkit.dev` / `*.wito.dev` → `authelia-authelia-proto` + `sso-bridge-authz`; else `authelia-authelia`. `localOnly` → `traefik-lan-only`, no public DNS.
Local Host must be `*.127.0.0.1.nip.io` (e.g. `http://example.127.0.0.1.nip.io:18080/`). Bare `127.0.0.1:18080` 404s.
## Local e2e
devenv + `./test/e2e.sh` (zellij: e2e / VM logs / k9s). `--no-zellij` for one terminal. Defaults: `apps/example{,-static,-web}`; `DEPLOY_NIX` for one. VM stays up (`KEEP_VM=1`); `--stop-vm` / `--cleanup-only` to tear down. SSH: `test/state/ssh/id_ed25519`. Forwards: 2222, 18080, 18443, 16443 (remap host ports, don't drop). VM `KUBECONFIG=/etc/rancher/k3s/k3s.yaml`. Links: `/run/issue`. State: `test/state/`.
## Mistakes
- `app.name` ≠ flake package / namespace
- `exec format error` → `CONTAINER_PLATFORM=linux/amd64`
- Local 404 → missing nip.io Host
- Static S3 404 → Garage website Host must equal the bucket alias (`app.domain` or `<name>.<rootDomain>`); S3Instance `garage` must exist
- Ingress stuck → issuer `default`, no `websecure`-only pin
- Empty env → missing GitLab `variables.NAME`
- Clone fails → job-token allowlist
- ImagePullBackOff after CI → group registry token
- LAN/SSO host on the internet → `localOnly` or `sso.enable` as intended
- `resources` / `replicas` copied from another app with no size or replica requirement
Read `modules/app.nix`, `templates/deploy/template.yml`, `ci/deploy.sh`, `apps/example*/deploy.nix`.