Skills Getting started Skills GitLab Hub

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`.