Compare commits
33 Commits
c548ead4f7
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 3016301729 | |||
| 027a9c675d | |||
| 14d267e12d | |||
| 2e8e11ecec | |||
| f90fa5c23b | |||
| caa6d0aafd | |||
| 29043896c8 | |||
| 1af9053cd5 | |||
| 2dcd03cbb0 | |||
| b5f0cdb429 | |||
| b63abca296 | |||
| 1311aadffb | |||
| f903ddeee5 | |||
| 33f3ddd7e9 | |||
| 1cdedf824c | |||
| beb856714e | |||
| fcb2067059 | |||
| cebd236b1f | |||
| 8cc818f6b2 | |||
| 305a7a5115 | |||
| 526888cd26 | |||
| 8d97d09b07 | |||
| 3f481e0a16 | |||
| 15dea7a249 | |||
| e1bace9044 | |||
| 09f2d2b013 | |||
| d195efdb0e | |||
| 3277c810a5 | |||
| f2baf3daf6 | |||
| 931470ee0a | |||
| 41b30788fe | |||
| 01ebff3596 | |||
| ed2c899915 |
96
.gitea/workflows/deploy-nomad.yaml
Normal file
96
.gitea/workflows/deploy-nomad.yaml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# ABOUTME: Reusable workflow for building Nix Docker images and deploying to Nomad.
|
||||||
|
# ABOUTME: Called by service repos with: uses: alo/alo-cluster/.gitea/workflows/deploy-nomad.yaml@master
|
||||||
|
|
||||||
|
name: Deploy to Nomad
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
service_name:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: "Nomad job name (must match job ID in services/*.hcl)"
|
||||||
|
flake_output:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: "dockerImage"
|
||||||
|
description: "Flake output to build (default: dockerImage)"
|
||||||
|
registry:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: "gitea.v.paler.net"
|
||||||
|
description: "Container registry hostname"
|
||||||
|
secrets:
|
||||||
|
REGISTRY_USERNAME:
|
||||||
|
required: true
|
||||||
|
REGISTRY_PASSWORD:
|
||||||
|
required: true
|
||||||
|
NOMAD_ADDR:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: nix
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
run: |
|
||||||
|
echo "Building .#${{ inputs.flake_output }}..."
|
||||||
|
nix build ".#${{ inputs.flake_output }}" --out-link result
|
||||||
|
|
||||||
|
- name: Push to registry
|
||||||
|
run: |
|
||||||
|
echo "Pushing to ${{ inputs.registry }}/alo/${{ inputs.service_name }}:latest..."
|
||||||
|
skopeo copy \
|
||||||
|
--dest-creds "${{ secrets.REGISTRY_USERNAME }}:${{ secrets.REGISTRY_PASSWORD }}" \
|
||||||
|
--insecure-policy \
|
||||||
|
docker-archive:result \
|
||||||
|
"docker://${{ inputs.registry }}/alo/${{ inputs.service_name }}:latest"
|
||||||
|
|
||||||
|
- name: Deploy to Nomad
|
||||||
|
env:
|
||||||
|
NOMAD_ADDR: ${{ secrets.NOMAD_ADDR }}
|
||||||
|
SERVICE: ${{ inputs.service_name }}
|
||||||
|
run: |
|
||||||
|
echo "Deploying $SERVICE to Nomad..."
|
||||||
|
|
||||||
|
# Fetch current job, update UUID to force deployment
|
||||||
|
JOB=$(curl -sS "$NOMAD_ADDR/v1/job/$SERVICE")
|
||||||
|
NEW_UUID=$(cat /proc/sys/kernel/random/uuid)
|
||||||
|
echo "New deployment UUID: $NEW_UUID"
|
||||||
|
UPDATED_JOB=$(echo "$JOB" | jq --arg uuid "$NEW_UUID" '.Meta.uuid = $uuid')
|
||||||
|
|
||||||
|
# Submit updated job
|
||||||
|
RESULT=$(echo "{\"Job\": $UPDATED_JOB}" | curl -sS -X POST "$NOMAD_ADDR/v1/jobs" \
|
||||||
|
-H "Content-Type: application/json" -d @-)
|
||||||
|
echo "Submit result: $RESULT"
|
||||||
|
|
||||||
|
# Monitor deployment
|
||||||
|
sleep 3
|
||||||
|
DEPLOY_ID=$(curl -sS "$NOMAD_ADDR/v1/job/$SERVICE/deployments" | jq -r '.[0].ID')
|
||||||
|
echo "Deployment ID: $DEPLOY_ID"
|
||||||
|
|
||||||
|
if [ "$DEPLOY_ID" = "null" ]; then
|
||||||
|
echo "ERROR: No deployment created. Ensure job has 'update' stanza with 'auto_revert = true'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Monitoring deployment..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
STATUS=$(curl -sS "$NOMAD_ADDR/v1/deployment/$DEPLOY_ID" | jq -r '.Status')
|
||||||
|
echo "[$i/30] Deployment status: $STATUS"
|
||||||
|
case $STATUS in
|
||||||
|
successful)
|
||||||
|
echo "Deployment successful!"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
failed|cancelled)
|
||||||
|
echo "Deployment failed or cancelled"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
echo "Timeout waiting for deployment"
|
||||||
|
exit 1
|
||||||
8
common/ham-radio.nix
Normal file
8
common/ham-radio.nix
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# ABOUTME: Ham radio tools profile for amateur radio operators.
|
||||||
|
# ABOUTME: Provides CLI tools for logging and processing ham radio contacts.
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.custom.flecli
|
||||||
|
];
|
||||||
|
}
|
||||||
206
docs/CICD_SETUP.md
Normal file
206
docs/CICD_SETUP.md
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
# CI/CD Setup for Nomad Services
|
||||||
|
|
||||||
|
Guide for adding automated builds and deployments to a service.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### 1. Service Repository
|
||||||
|
|
||||||
|
Your service needs a `flake.nix` that exports a Docker image:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
outputs = { self, nixpkgs, ... }: {
|
||||||
|
# The workflow looks for this output by default
|
||||||
|
dockerImage = pkgs.dockerTools.buildImage {
|
||||||
|
name = "gitea.v.paler.net/alo/<service>";
|
||||||
|
tag = "latest";
|
||||||
|
# ... image config
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: Use `extraCommands` instead of `runAsRoot` in your Docker build - the CI runner doesn't have KVM.
|
||||||
|
|
||||||
|
### 2. Nomad Job
|
||||||
|
|
||||||
|
Your job in `services/<name>.hcl` needs:
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
job "<service>" {
|
||||||
|
# Required: UUID changes trigger deployments
|
||||||
|
meta {
|
||||||
|
uuid = uuidv4()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Required: enables deployment tracking and auto-rollback
|
||||||
|
update {
|
||||||
|
max_parallel = 1
|
||||||
|
health_check = "checks"
|
||||||
|
min_healthy_time = "30s"
|
||||||
|
healthy_deadline = "5m"
|
||||||
|
auto_revert = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Required: pulls new image on each deployment
|
||||||
|
task "app" {
|
||||||
|
config {
|
||||||
|
force_pull = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Recommended: health check for deployment validation
|
||||||
|
service {
|
||||||
|
check {
|
||||||
|
type = "http"
|
||||||
|
path = "/healthz"
|
||||||
|
interval = "10s"
|
||||||
|
timeout = "5s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Create Workflow
|
||||||
|
|
||||||
|
Add `.gitea/workflows/deploy.yaml` to your service repo:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
uses: alo/alo-cluster/.gitea/workflows/deploy-nomad.yaml@master
|
||||||
|
with:
|
||||||
|
service_name: <your-service> # Must match Nomad job ID
|
||||||
|
secrets: inherit
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Add Secrets
|
||||||
|
|
||||||
|
In Gitea → Your Repo → Settings → Actions → Secrets, add:
|
||||||
|
|
||||||
|
| Secret | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| `REGISTRY_USERNAME` | Your Gitea username |
|
||||||
|
| `REGISTRY_PASSWORD` | Gitea access token with `packages:write` |
|
||||||
|
| `NOMAD_ADDR` | `http://nomad.service.consul:4646` |
|
||||||
|
|
||||||
|
### 3. Push
|
||||||
|
|
||||||
|
Push to `master` branch. The workflow will:
|
||||||
|
1. Build your Docker image with Nix
|
||||||
|
2. Push to Gitea registry
|
||||||
|
3. Update the Nomad job to trigger deployment
|
||||||
|
4. Monitor until deployment succeeds or fails
|
||||||
|
|
||||||
|
## Workflow Options
|
||||||
|
|
||||||
|
The shared workflow accepts these inputs:
|
||||||
|
|
||||||
|
| Input | Default | Description |
|
||||||
|
|-------|---------|-------------|
|
||||||
|
| `service_name` | (required) | Nomad job ID |
|
||||||
|
| `flake_output` | `dockerImage` | Flake output to build |
|
||||||
|
| `registry` | `gitea.v.paler.net` | Container registry |
|
||||||
|
|
||||||
|
Example with custom flake output:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
uses: alo/alo-cluster/.gitea/workflows/deploy-nomad.yaml@master
|
||||||
|
with:
|
||||||
|
service_name: myservice
|
||||||
|
flake_output: packages.x86_64-linux.docker
|
||||||
|
secrets: inherit
|
||||||
|
```
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
```
|
||||||
|
Push to master
|
||||||
|
↓
|
||||||
|
Build: nix build .#dockerImage
|
||||||
|
↓
|
||||||
|
Push: skopeo → gitea.v.paler.net/alo/<service>:latest
|
||||||
|
↓
|
||||||
|
Deploy: Update job meta.uuid → Nomad creates deployment
|
||||||
|
↓
|
||||||
|
Monitor: Poll deployment status for up to 5 minutes
|
||||||
|
↓
|
||||||
|
Success: Deployment healthy
|
||||||
|
OR
|
||||||
|
Failure: Nomad auto-reverts to previous version
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Build fails with KVM error
|
||||||
|
|
||||||
|
```
|
||||||
|
Required system: 'x86_64-linux' with features {kvm}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `extraCommands` instead of `runAsRoot` in your `docker.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# Bad - requires KVM
|
||||||
|
runAsRoot = ''
|
||||||
|
mkdir -p /tmp
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Good - no KVM needed
|
||||||
|
extraCommands = ''
|
||||||
|
mkdir -p tmp
|
||||||
|
chmod 1777 tmp
|
||||||
|
'';
|
||||||
|
```
|
||||||
|
|
||||||
|
### No deployment created
|
||||||
|
|
||||||
|
Ensure your Nomad job has the `update` stanza with `auto_revert = true`.
|
||||||
|
|
||||||
|
### Image not updating
|
||||||
|
|
||||||
|
Check that `force_pull = true` is set in the Nomad job's Docker config.
|
||||||
|
|
||||||
|
### Deployment fails health checks
|
||||||
|
|
||||||
|
- Check your `/healthz` endpoint works
|
||||||
|
- Increase `healthy_deadline` if startup is slow
|
||||||
|
- Check `nomad alloc logs <alloc-id>` for errors
|
||||||
|
|
||||||
|
### Workflow can't access alo-cluster
|
||||||
|
|
||||||
|
If Gitea can't pull the reusable workflow, you may need to make alo-cluster public or use a token. As a fallback, copy the workflow content directly.
|
||||||
|
|
||||||
|
## Manual Deployment
|
||||||
|
|
||||||
|
If CI fails, you can deploy manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd <service-repo>
|
||||||
|
nix build .#dockerImage
|
||||||
|
skopeo copy --dest-authfile ~/.docker/config.json \
|
||||||
|
docker-archive:result \
|
||||||
|
docker://gitea.v.paler.net/alo/<service>:latest
|
||||||
|
nomad run /path/to/alo-cluster/services/<service>.hcl
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
Nomad auto-reverts on health check failure. For manual rollback:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nomad job history <service> # List versions
|
||||||
|
nomad job revert <service> <version> # Revert to specific version
|
||||||
|
```
|
||||||
@@ -4,4 +4,4 @@
|
|||||||
* renovate system of some kind
|
* renovate system of some kind
|
||||||
* vector (or other log ingestion) everywhere, consider moving it off docker if possible
|
* vector (or other log ingestion) everywhere, consider moving it off docker if possible
|
||||||
* monitor backup-persist success/fail
|
* monitor backup-persist success/fail
|
||||||
|
* gitea organization is public -> at least from the internal network, anyone can pull images and probably also clone repos. there should be absolutely zero secrets in the repos (and the ones that are now should be changed before stored somewhere else) and the nomad workers should authenticate to pull images
|
||||||
|
|||||||
153
flake.lock
generated
153
flake.lock
generated
@@ -25,11 +25,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766518262,
|
"lastModified": 1769196967,
|
||||||
"narHash": "sha256-ITihWSOexQX1a/i8ZiMt3M609KxX7sLhz63F9rnnguY=",
|
"narHash": "sha256-js2jXLzaZbXNFkYTszQntIS8QUJYJumSFK+3bR5nhlo=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "browser-previews",
|
"repo": "browser-previews",
|
||||||
"rev": "1e4c4310443ace75d0df9efaeb4c6fc6f8c8678c",
|
"rev": "edc3b1c0455abc74bfe2d6e029abe5fc778b0d62",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -68,11 +68,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1764011051,
|
"lastModified": 1768818222,
|
||||||
"narHash": "sha256-M7SZyPZiqZUR/EiiBJnmyUbOi5oE/03tCeFrTiUZchI=",
|
"narHash": "sha256-460jc0+CZfyaO8+w8JNtlClB2n4ui1RbHfPTLkpwhU8=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "devshell",
|
"repo": "devshell",
|
||||||
"rev": "17ed8d9744ebe70424659b0ef74ad6d41fc87071",
|
"rev": "255a2b1725a20d060f566e4755dbf571bbbb5f76",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -88,11 +88,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766150702,
|
"lastModified": 1768923567,
|
||||||
"narHash": "sha256-P0kM+5o+DKnB6raXgFEk3azw8Wqg5FL6wyl9jD+G5a4=",
|
"narHash": "sha256-GVJ0jKsyXLuBzRMXCDY6D5J8wVdwP1DuQmmvYL/Vw/Q=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "916506443ecd0d0b4a0f4cf9d40a3c22ce39b378",
|
"rev": "00395d188e3594a1507f214a2f15d4ce5c07cb28",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -115,11 +115,11 @@
|
|||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766221270,
|
"lastModified": 1769298686,
|
||||||
"narHash": "sha256-YDNOESYOfafsoqW7qEM1Gkuv6DZVZyn6Wrs7+/I7u0A=",
|
"narHash": "sha256-ZwsxXeLyrb5VinFsdjrjt/J7Tp5O2A9yy7lxWaw/h78=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "ethereum.nix",
|
"repo": "ethereum.nix",
|
||||||
"rev": "cd43a1333b8a2e519815d468b791b893d103e618",
|
"rev": "d52663e0592ced611098f80224b45e57d7223453",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -149,11 +149,11 @@
|
|||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1763759067,
|
"lastModified": 1768135262,
|
||||||
"narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=",
|
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0",
|
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -170,11 +170,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1765835352,
|
"lastModified": 1768135262,
|
||||||
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
|
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
|
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -237,11 +237,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1764753179,
|
"lastModified": 1767517855,
|
||||||
"narHash": "sha256-2u7C/aKEcCpND60JfREYhKaMg/0cZ/pk1HviizWrCl8=",
|
"narHash": "sha256-LnZosb07bahYAyFw07JFzSXslx9j1dCe+npWDZdPFZg=",
|
||||||
"owner": "shazow",
|
"owner": "shazow",
|
||||||
"repo": "foundry.nix",
|
"repo": "foundry.nix",
|
||||||
"rev": "8c21d3ac7a03da27af55d3c94fe95620c9d6f316",
|
"rev": "ee376e8a93f537c2865dda9811e748e4567a7aaf",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -258,11 +258,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766553861,
|
"lastModified": 1768949235,
|
||||||
"narHash": "sha256-ZbnG01yA3O8Yr1vUm3+NQ2qk9iRhS5bloAnuXHHy7+c=",
|
"narHash": "sha256-TtjKgXyg1lMfh374w5uxutd6Vx2P/hU81aEhTxrO2cg=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "0999ed8f965bbbd991437ad9c5ed3434cecbc30e",
|
"rev": "75ed713570ca17427119e7e204ab3590cc3bf2a5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -272,13 +272,38 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"impermanence": {
|
"home-manager_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"impermanence",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1737831083,
|
"lastModified": 1768598210,
|
||||||
"narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=",
|
"narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"rev": "c47b2cc64a629f8e075de52e4742de688f930dc6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"impermanence": {
|
||||||
|
"inputs": {
|
||||||
|
"home-manager": "home-manager_2",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1768941735,
|
||||||
|
"narHash": "sha256-OyxsfXNcOkt06/kM+4bnuC8moDx+t7Qr+RB0BBa83Ig=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "impermanence",
|
"repo": "impermanence",
|
||||||
"rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170",
|
"rev": "69ecf31e8fddc9354a4b418f3a517445d486bb54",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -328,11 +353,11 @@
|
|||||||
},
|
},
|
||||||
"nixos-hardware": {
|
"nixos-hardware": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766568855,
|
"lastModified": 1769302137,
|
||||||
"narHash": "sha256-UXVtN77D7pzKmzOotFTStgZBqpOcf8cO95FcupWp4Zo=",
|
"narHash": "sha256-QEDtctEkOsbx8nlFh4yqPEOtr4tif6KTqWwJ37IM2ds=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "c5db9569ac9cc70929c268ac461f4003e3e5ca80",
|
"rev": "a351494b0e35fd7c0b7a1aae82f0afddf4907aa8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -344,27 +369,27 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766622938,
|
"lastModified": 1768564909,
|
||||||
"narHash": "sha256-Eovt/DOCYjFFBZuYbbG9j5jhklzxdNbUGVYYxh3lG3s=",
|
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
|
||||||
"owner": "NixOS",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "5900a0a8850cbba98e16d5a7a6ed389402dfcf4f",
|
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "nixos",
|
||||||
"ref": "nixos-25.11",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-lib": {
|
"nixpkgs-lib": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761765539,
|
"lastModified": 1765674936,
|
||||||
"narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
|
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixpkgs.lib",
|
"repo": "nixpkgs.lib",
|
||||||
"rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
|
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -390,11 +415,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1765270179,
|
"lastModified": 1769092226,
|
||||||
"narHash": "sha256-g2a4MhRKu4ymR4xwo+I+auTknXt/+j37Lnf0Mvfl1rE=",
|
"narHash": "sha256-6h5sROT/3CTHvzPy9koKBmoCa2eJKh4fzQK8eYFEgl8=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "677fbe97984e7af3175b6c121f3c39ee5c8d62c9",
|
"rev": "b579d443b37c9c5373044201ea77604e37e748c8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -406,11 +431,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable_2": {
|
"nixpkgs-unstable_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766651565,
|
"lastModified": 1769170682,
|
||||||
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
|
"narHash": "sha256-oMmN1lVQU0F0W2k6OI3bgdzp2YOHWYUAw79qzDSjenU=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
|
"rev": "c5296fdd05cfa2c187990dd909864da9658df755",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -420,6 +445,22 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769089682,
|
||||||
|
"narHash": "sha256-9yA/LIuAVQq0lXelrZPjLuLVuZdm03p8tfmHhnDIkms=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "078d69f03934859a181e81ba987c2bb033eebfc5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-25.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixvim": {
|
"nixvim": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts_2",
|
"flake-parts": "flake-parts_2",
|
||||||
@@ -429,11 +470,11 @@
|
|||||||
"systems": "systems_4"
|
"systems": "systems_4"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766721995,
|
"lastModified": 1769247851,
|
||||||
"narHash": "sha256-2qZLSojZFP3AzbC6UNF3ASCIDLahNniR2XP7l/qINm4=",
|
"narHash": "sha256-fbsopU0qWfqq1WRKjWYpYCMxmEYyq+Cmw++VXVke5Ns=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixvim",
|
"repo": "nixvim",
|
||||||
"rev": "66a5dc70e2d8433034bccdbb9c3c7bcecd86f9a6",
|
"rev": "34a7d94cdcd2b034eb06202992bed1345aa046c9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -453,7 +494,7 @@
|
|||||||
"nix-colors": "nix-colors",
|
"nix-colors": "nix-colors",
|
||||||
"nix-index-database": "nix-index-database",
|
"nix-index-database": "nix-index-database",
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable_2",
|
"nixpkgs-unstable": "nixpkgs-unstable_2",
|
||||||
"nixvim": "nixvim",
|
"nixvim": "nixvim",
|
||||||
"sops-nix": "sops-nix"
|
"sops-nix": "sops-nix"
|
||||||
@@ -466,11 +507,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766289575,
|
"lastModified": 1769314333,
|
||||||
"narHash": "sha256-BOKCwOQQIP4p9z8DasT5r+qjri3x7sPCOq+FTjY8Z+o=",
|
"narHash": "sha256-+Uvq9h2eGsbhacXpuS7irYO7fFlz514nrhPCSTkASlw=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "9836912e37aef546029e48c8749834735a6b9dad",
|
"rev": "2eb9eed7ef48908e0f02985919f7eb9d33fa758f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -547,11 +588,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766000401,
|
"lastModified": 1768158989,
|
||||||
"narHash": "sha256-+cqN4PJz9y0JQXfAK5J1drd0U05D5fcAGhzhfVrDlsI=",
|
"narHash": "sha256-67vyT1+xClLldnumAzCTBvU0jLZ1YBcf4vANRWP3+Ak=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "treefmt-nix",
|
"repo": "treefmt-nix",
|
||||||
"rev": "42d96e75aa56a3f70cab7e7dc4a32868db28e8fd",
|
"rev": "e96d59dff5c0d7fddb9d113ba108f03c3ef99eca",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -58,15 +58,17 @@
|
|||||||
|
|
||||||
overlay-unstable = final: prev: {
|
overlay-unstable = final: prev: {
|
||||||
unstable = import nixpkgs-unstable {
|
unstable = import nixpkgs-unstable {
|
||||||
inherit (prev) system;
|
system = prev.stdenv.hostPlatform.system;
|
||||||
config.allowUnfree = true;
|
config.allowUnfree = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
overlay-browser-previews = final: prev: {
|
overlay-browser-previews = final: prev: {
|
||||||
browser-previews = browser-previews.packages.${prev.system};
|
browser-previews = browser-previews.packages.${prev.stdenv.hostPlatform.system};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
overlay-custom = import ./pkgs;
|
||||||
|
|
||||||
mkHost =
|
mkHost =
|
||||||
system: profile: modules:
|
system: profile: modules:
|
||||||
let
|
let
|
||||||
@@ -79,7 +81,7 @@
|
|||||||
(
|
(
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
nixpkgs.overlays = [ overlay-unstable overlay-browser-previews ];
|
nixpkgs.overlays = [ overlay-unstable overlay-browser-previews overlay-custom ];
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ let
|
|||||||
cliPkgs = with pkgs; [
|
cliPkgs = with pkgs; [
|
||||||
ast-grep
|
ast-grep
|
||||||
yq
|
yq
|
||||||
|
unstable.amp-cli
|
||||||
|
unstable.beads
|
||||||
unstable.claude-code
|
unstable.claude-code
|
||||||
unstable.codex
|
unstable.codex
|
||||||
unstable.gemini-cli
|
unstable.gemini-cli
|
||||||
|
|||||||
@@ -15,9 +15,5 @@
|
|||||||
natural_scroll = false;
|
natural_scroll = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
gestures = lib.mkDefault {
|
|
||||||
workspace_swipe = false;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,6 +339,16 @@
|
|||||||
set pure_show_prefix_root_prompt true
|
set pure_show_prefix_root_prompt true
|
||||||
set sponge_regex_patterns 'password|passwd'
|
set sponge_regex_patterns 'password|passwd'
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
functions = {
|
||||||
|
brain = ''
|
||||||
|
echo "🧠 Brain session starting..."
|
||||||
|
echo " • wrap - end session with notes"
|
||||||
|
echo " • inbox: <thought> - quick capture"
|
||||||
|
echo ""
|
||||||
|
cd ~/brain && claude
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fzf = {
|
fzf = {
|
||||||
@@ -347,8 +357,12 @@
|
|||||||
|
|
||||||
git = {
|
git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
userEmail = "petru@paler.net";
|
settings = {
|
||||||
userName = "Petru Paler";
|
user = {
|
||||||
|
email = "petru@paler.net";
|
||||||
|
name = "Petru Paler";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
# Desktop environment is imported via flake.nix for desktop profile
|
# Desktop environment is imported via flake.nix for desktop profile
|
||||||
../../common/cluster-member.nix # Consul + storage clients
|
../../common/cluster-member.nix # Consul + storage clients
|
||||||
../../common/cluster-tools.nix # Nomad CLI (no service)
|
../../common/cluster-tools.nix # Nomad CLI (no service)
|
||||||
|
../../common/docker.nix # Docker daemon
|
||||||
|
../../common/ham-radio.nix # Ham radio tools (FLEcli)
|
||||||
./hardware.nix
|
./hardware.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
100
nix-runner/README.md
Normal file
100
nix-runner/README.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Nix Runner for Gitea Actions
|
||||||
|
|
||||||
|
Custom Docker image for running Nix builds in CI.
|
||||||
|
|
||||||
|
## What's Included
|
||||||
|
|
||||||
|
- **Nix** with flakes enabled (`experimental-features = nix-command flakes`)
|
||||||
|
- **Node.js 20** for JavaScript-based GitHub Actions
|
||||||
|
- **Tools**: git, curl, jq, skopeo, bash, coreutils
|
||||||
|
- **Binary caches**:
|
||||||
|
- `c3.mule-stork.ts.net:8501` (local cache proxy)
|
||||||
|
- `cache.nixos.org` (official)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
In your workflow:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: nix
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: nix build .#myPackage
|
||||||
|
```
|
||||||
|
|
||||||
|
The `nix` label is configured in `services/act-runner.hcl`.
|
||||||
|
|
||||||
|
## Current Version
|
||||||
|
|
||||||
|
**Tag**: `v4`
|
||||||
|
**Image**: `gitea.v.paler.net/alo/nix-runner:v4`
|
||||||
|
|
||||||
|
## Updating the Runner
|
||||||
|
|
||||||
|
### 1. Edit `flake.nix`
|
||||||
|
|
||||||
|
Make your changes, then bump the tag:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
tag = "v5"; # was v4
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd nix-runner
|
||||||
|
nix build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Push to Registry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
skopeo copy --dest-authfile ~/.docker/config.json \
|
||||||
|
docker-archive:result \
|
||||||
|
docker://gitea.v.paler.net/alo/nix-runner:v5
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Update act-runner
|
||||||
|
|
||||||
|
Edit `services/act-runner.hcl`:
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
GITEA_RUNNER_LABELS = "ubuntu-latest:docker://node:20-bookworm,nix:docker://gitea.v.paler.net/alo/nix-runner:v5"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Re-register Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo rm /data/services/act-runner/.runner
|
||||||
|
nomad run services/act-runner.hcl
|
||||||
|
```
|
||||||
|
|
||||||
|
The runner will re-register with the new labels.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The image uses `NIX_CONFIG` environment variable for Nix settings:
|
||||||
|
|
||||||
|
```
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
sandbox = false
|
||||||
|
build-users-group =
|
||||||
|
substituters = http://c3.mule-stork.ts.net:8501 https://cache.nixos.org
|
||||||
|
trusted-public-keys = cache.nixos.org-1:... c3:...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Build fails with `build-users-group` error
|
||||||
|
|
||||||
|
The image runs as root without the nixbld group. This is handled by `build-users-group =` in NIX_CONFIG.
|
||||||
|
|
||||||
|
### Can't fetch from cache
|
||||||
|
|
||||||
|
Check that the runner container can reach `c3.mule-stork.ts.net:8501` (Tailscale network).
|
||||||
|
|
||||||
|
### Missing tool
|
||||||
|
|
||||||
|
Add it to `paths` in `flake.nix` and rebuild/push a new version.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
in {
|
in {
|
||||||
packages.default = pkgs.dockerTools.buildImage {
|
packages.default = pkgs.dockerTools.buildImage {
|
||||||
name = "gitea.v.paler.net/ppetru/nix-runner";
|
name = "gitea.v.paler.net/alo/nix-runner";
|
||||||
tag = "v4";
|
tag = "v4";
|
||||||
|
|
||||||
copyToRoot = pkgs.buildEnv {
|
copyToRoot = pkgs.buildEnv {
|
||||||
|
|||||||
7
pkgs/default.nix
Normal file
7
pkgs/default.nix
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# ABOUTME: Custom packages overlay for packages not in nixpkgs.
|
||||||
|
# ABOUTME: Adds packages under pkgs.custom.* namespace.
|
||||||
|
final: prev: {
|
||||||
|
custom = {
|
||||||
|
flecli = final.callPackage ./flecli.nix { };
|
||||||
|
};
|
||||||
|
}
|
||||||
28
pkgs/flecli.nix
Normal file
28
pkgs/flecli.nix
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# ABOUTME: FLEcli - Fast Log Entry CLI for amateur radio logging.
|
||||||
|
# ABOUTME: Processes FLE-formatted files into ADIF for SOTA, POTA, WWFF, etc.
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
buildGoModule,
|
||||||
|
fetchFromGitHub,
|
||||||
|
}:
|
||||||
|
|
||||||
|
buildGoModule rec {
|
||||||
|
pname = "flecli";
|
||||||
|
version = "0.1.7";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "on4kjm";
|
||||||
|
repo = "FLEcli";
|
||||||
|
rev = "v${version}";
|
||||||
|
hash = "sha256-6OFcShgUaK9RwonP6cl8eOD6Cu+F5LHZEUFPjCfWNV0=";
|
||||||
|
};
|
||||||
|
|
||||||
|
vendorHash = "sha256-6m01rcewPyy8pUXnIMwjyW+7I08pyJaTDliwTNp3fOM=";
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "Fast Log Entry CLI - process amateur radio logs";
|
||||||
|
homepage = "https://github.com/on4kjm/FLEcli";
|
||||||
|
license = licenses.mit;
|
||||||
|
mainProgram = "FLEcli";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@ EOH
|
|||||||
|
|
||||||
env {
|
env {
|
||||||
GITEA_INSTANCE_URL = "https://gitea.v.paler.net"
|
GITEA_INSTANCE_URL = "https://gitea.v.paler.net"
|
||||||
GITEA_RUNNER_LABELS = "ubuntu-latest:docker://node:20-bookworm,nix:docker://gitea.v.paler.net/ppetru/nix-runner:v4"
|
GITEA_RUNNER_LABELS = "ubuntu-latest:docker://node:20-bookworm,nix:docker://gitea.v.paler.net/alo/nix-runner:v4"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Template needed for nomadVar interpolation (secrets) and Nomad runtime vars
|
# Template needed for nomadVar interpolation (secrets) and Nomad runtime vars
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ job "animaltrack" {
|
|||||||
user = "1000"
|
user = "1000"
|
||||||
|
|
||||||
config {
|
config {
|
||||||
image = "gitea.v.paler.net/ppetru/animaltrack:latest"
|
image = "gitea.v.paler.net/alo/animaltrack:latest"
|
||||||
ports = ["http"]
|
ports = ["http"]
|
||||||
force_pull = true
|
force_pull = true
|
||||||
volumes = ["/data/services/animaltrack:/var/lib/animaltrack"]
|
volumes = ["/data/services/animaltrack:/var/lib/animaltrack"]
|
||||||
@@ -68,6 +68,7 @@ EOH
|
|||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.animaltrack.entryPoints=websecure",
|
"traefik.http.routers.animaltrack.entryPoints=websecure",
|
||||||
"traefik.http.routers.animaltrack.middlewares=oidc-auth@file",
|
"traefik.http.routers.animaltrack.middlewares=oidc-auth@file",
|
||||||
|
"traefik.http.routers.animaltrack.rule=Host(`farm.alo.land`)",
|
||||||
]
|
]
|
||||||
|
|
||||||
check {
|
check {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ job "beancount" {
|
|||||||
user = "1000"
|
user = "1000"
|
||||||
|
|
||||||
config {
|
config {
|
||||||
image = "gitea.v.paler.net/ppetru/fava:latest"
|
image = "gitea.v.paler.net/alo/fava:latest"
|
||||||
ports = ["http"]
|
ports = ["http"]
|
||||||
volumes = [
|
volumes = [
|
||||||
"/data/services/beancount:/beancount",
|
"/data/services/beancount:/beancount",
|
||||||
|
|||||||
50
services/brain.hcl
Normal file
50
services/brain.hcl
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# ABOUTME: Brain - Petre's externalized executive function system
|
||||||
|
# ABOUTME: SilverBullet for markdown web UI + ttyd for web terminal
|
||||||
|
|
||||||
|
job "brain" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
group "web" {
|
||||||
|
volume "services" {
|
||||||
|
type = "host"
|
||||||
|
read_only = false
|
||||||
|
source = "services"
|
||||||
|
}
|
||||||
|
|
||||||
|
network {
|
||||||
|
port "silverbullet" {
|
||||||
|
to = 3000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "silverbullet" {
|
||||||
|
driver = "docker"
|
||||||
|
user = "1000"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "zefhemel/silverbullet:latest"
|
||||||
|
ports = ["silverbullet"]
|
||||||
|
volumes = ["/data/services/brain:/space"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
memory = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "brain"
|
||||||
|
port = "silverbullet"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.brain.entryPoints=websecure",
|
||||||
|
"traefik.http.routers.brain.middlewares=oidc-auth@file",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: terminal task with ttyd for web-based amp/claude access
|
||||||
|
# Needs custom image with tmux + amp + claude-code installed
|
||||||
|
# For now, use SSH or local terminal for amp sessions
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
job "farmos" {
|
|
||||||
datacenters = ["alo"]
|
|
||||||
|
|
||||||
meta {
|
|
||||||
uuid = uuidv4()
|
|
||||||
}
|
|
||||||
|
|
||||||
group "os" {
|
|
||||||
network {
|
|
||||||
port "http" {
|
|
||||||
to = 80
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task "server" {
|
|
||||||
driver = "docker"
|
|
||||||
|
|
||||||
config {
|
|
||||||
image = "gitea.v.paler.net/ppetru/farmos:latest"
|
|
||||||
ports = ["http"]
|
|
||||||
volumes = [
|
|
||||||
"/data/services/farmos/sites:/opt/drupal/web/sites",
|
|
||||||
"/data/services/farmos/keys:/opt/drupal/keys",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
service {
|
|
||||||
name = "farmos"
|
|
||||||
port = "http"
|
|
||||||
check {
|
|
||||||
type = "http"
|
|
||||||
port = "http"
|
|
||||||
path = "/health"
|
|
||||||
interval = "30s"
|
|
||||||
timeout = "2s"
|
|
||||||
}
|
|
||||||
|
|
||||||
tags = [
|
|
||||||
"traefik.enable=true",
|
|
||||||
"traefik.http.routers.farmos.entryPoints=websecure",
|
|
||||||
"traefik.http.routers.farmos.rule=Host(`farm.alo.land`)",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
resources {
|
|
||||||
cpu = 2000
|
|
||||||
memory = 1024
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
75
services/fitdata.hcl
Normal file
75
services/fitdata.hcl
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# ABOUTME: Nomad job for Fitdata - fitness data analysis and MCP server.
|
||||||
|
# ABOUTME: Runs FastHTML Python app with SQLite, static port for MCP access.
|
||||||
|
|
||||||
|
# Setup required before running:
|
||||||
|
# sudo mkdir -p /data/services/fitdata && sudo chown 1000:1000 /data/services/fitdata
|
||||||
|
|
||||||
|
job "fitdata" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
meta {
|
||||||
|
uuid = uuidv4()
|
||||||
|
}
|
||||||
|
|
||||||
|
update {
|
||||||
|
max_parallel = 1
|
||||||
|
health_check = "checks"
|
||||||
|
min_healthy_time = "30s"
|
||||||
|
healthy_deadline = "5m"
|
||||||
|
progress_deadline = "10m"
|
||||||
|
auto_revert = true
|
||||||
|
}
|
||||||
|
|
||||||
|
group "web" {
|
||||||
|
network {
|
||||||
|
port "http" {
|
||||||
|
static = 5311
|
||||||
|
to = 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "app" {
|
||||||
|
driver = "docker"
|
||||||
|
user = "1000"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "gitea.v.paler.net/alo/fitdata:latest"
|
||||||
|
ports = ["http"]
|
||||||
|
force_pull = true
|
||||||
|
volumes = ["/data/services/fitdata:/var/lib/fitdata"]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
FITDATA_DATA_DIR = "/var/lib/fitdata"
|
||||||
|
FITDATA_DB_PATH = "/var/lib/fitdata/fitdata.db"
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
memory = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "fitdata"
|
||||||
|
port = "http"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.fitdata.entryPoints=websecure",
|
||||||
|
"traefik.http.routers.fitdata.middlewares=oidc-auth@file",
|
||||||
|
]
|
||||||
|
|
||||||
|
check {
|
||||||
|
type = "http"
|
||||||
|
path = "/healthz"
|
||||||
|
interval = "10s"
|
||||||
|
timeout = "5s"
|
||||||
|
|
||||||
|
check_restart {
|
||||||
|
limit = 3
|
||||||
|
grace = "60s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ job "igsync" {
|
|||||||
driver = "docker"
|
driver = "docker"
|
||||||
|
|
||||||
config {
|
config {
|
||||||
image = "gitea.v.paler.net/ppetru/igsync:latest"
|
image = "gitea.v.paler.net/alo/igsync:latest"
|
||||||
|
|
||||||
# Mount the data directory for .env, database, and media files
|
# Mount the data directory for .env, database, and media files
|
||||||
volumes = [
|
volumes = [
|
||||||
|
|||||||
138
services/phaseflow.hcl
Normal file
138
services/phaseflow.hcl
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# ABOUTME: Nomad job for PhaseFlow - menstrual cycle tracking and training app.
|
||||||
|
# ABOUTME: Runs Next.js app with PocketBase sidecar for data persistence.
|
||||||
|
|
||||||
|
# Setup required before running:
|
||||||
|
# sudo mkdir -p /data/services/phaseflow/pb_data && sudo chown 1000:1000 /data/services/phaseflow /data/services/phaseflow/pb_data
|
||||||
|
# nomad var put secrets/phaseflow \
|
||||||
|
# mailgun_api_key="key-xxxx" \
|
||||||
|
# mailgun_domain="paler.net" \
|
||||||
|
# encryption_key="$(openssl rand -hex 16)" \
|
||||||
|
# cron_secret="$(openssl rand -hex 32)" \
|
||||||
|
# pocketbase_admin_email="admin@example.com" \
|
||||||
|
# pocketbase_admin_password="your-admin-password"
|
||||||
|
|
||||||
|
job "phaseflow" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
# Force re-pull of :latest images on each nomad run
|
||||||
|
meta {
|
||||||
|
uuid = uuidv4()
|
||||||
|
}
|
||||||
|
|
||||||
|
update {
|
||||||
|
max_parallel = 1
|
||||||
|
health_check = "checks"
|
||||||
|
min_healthy_time = "30s"
|
||||||
|
healthy_deadline = "5m"
|
||||||
|
progress_deadline = "10m"
|
||||||
|
auto_revert = true
|
||||||
|
}
|
||||||
|
|
||||||
|
group "app" {
|
||||||
|
network {
|
||||||
|
port "http" {
|
||||||
|
to = 3000
|
||||||
|
}
|
||||||
|
port "pocketbase" {
|
||||||
|
to = 8090
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# PocketBase sidecar - starts first, provides database
|
||||||
|
task "pocketbase" {
|
||||||
|
driver = "docker"
|
||||||
|
user = "1000"
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
hook = "prestart"
|
||||||
|
sidecar = true
|
||||||
|
}
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "ghcr.io/muchobien/pocketbase:latest"
|
||||||
|
ports = ["pocketbase"]
|
||||||
|
volumes = ["/data/services/phaseflow/pb_data:/pb_data"]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
PB_DATA_DIR = "/pb_data"
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
memory = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "pocketbase-phaseflow"
|
||||||
|
port = "pocketbase"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.pocketbase-phaseflow.entryPoints=websecure",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main Next.js application
|
||||||
|
task "app" {
|
||||||
|
driver = "docker"
|
||||||
|
user = "1000"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "gitea.v.paler.net/alo/phaseflow:latest"
|
||||||
|
ports = ["http"]
|
||||||
|
force_pull = true
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
NODE_ENV = "production"
|
||||||
|
POCKETBASE_URL = "http://${NOMAD_ADDR_pocketbase}"
|
||||||
|
NEXT_PUBLIC_POCKETBASE_URL = "https://pocketbase-phaseflow.v.paler.net"
|
||||||
|
APP_URL = "https://phaseflow.v.paler.net"
|
||||||
|
EMAIL_FROM = "phaseflow@paler.net"
|
||||||
|
MAILGUN_URL = "https://api.eu.mailgun.net"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Secrets from Nomad variables
|
||||||
|
template {
|
||||||
|
destination = "secrets/env.env"
|
||||||
|
env = true
|
||||||
|
data = <<EOH
|
||||||
|
MAILGUN_API_KEY={{ with nomadVar "secrets/phaseflow" }}{{ .mailgun_api_key }}{{ end }}
|
||||||
|
MAILGUN_DOMAIN={{ with nomadVar "secrets/phaseflow" }}{{ .mailgun_domain }}{{ end }}
|
||||||
|
ENCRYPTION_KEY={{ with nomadVar "secrets/phaseflow" }}{{ .encryption_key }}{{ end }}
|
||||||
|
CRON_SECRET={{ with nomadVar "secrets/phaseflow" }}{{ .cron_secret }}{{ end }}
|
||||||
|
POCKETBASE_ADMIN_EMAIL={{ with nomadVar "secrets/phaseflow" }}{{ .pocketbase_admin_email }}{{ end }}
|
||||||
|
POCKETBASE_ADMIN_PASSWORD={{ with nomadVar "secrets/phaseflow" }}{{ .pocketbase_admin_password }}{{ end }}
|
||||||
|
EOH
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
memory = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "phaseflow"
|
||||||
|
port = "http"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.phaseflow.entryPoints=websecure",
|
||||||
|
"metrics",
|
||||||
|
]
|
||||||
|
|
||||||
|
check {
|
||||||
|
type = "http"
|
||||||
|
path = "/api/health"
|
||||||
|
interval = "10s"
|
||||||
|
timeout = "5s"
|
||||||
|
|
||||||
|
check_restart {
|
||||||
|
limit = 3
|
||||||
|
grace = "60s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,10 @@
|
|||||||
job "urbit" {
|
job "urbit" {
|
||||||
datacenters = ["alo"]
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
meta {
|
||||||
|
uuid = uuidv4()
|
||||||
|
}
|
||||||
|
|
||||||
group "os" {
|
group "os" {
|
||||||
network {
|
network {
|
||||||
port "http" {
|
port "http" {
|
||||||
@@ -25,7 +29,7 @@ job "urbit" {
|
|||||||
# You can also set a variable loom size (Urbit memory size) using
|
# You can also set a variable loom size (Urbit memory size) using
|
||||||
# --loom=$LOOM_SIZE. Passing /bin/start-urbit --loom=32 for example, would set up
|
# --loom=$LOOM_SIZE. Passing /bin/start-urbit --loom=32 for example, would set up
|
||||||
# a 4GiB loom (2^32 bytes = 4GiB). The default loom size is 31 (2GiB).
|
# a 4GiB loom (2^32 bytes = 4GiB). The default loom size is 31 (2GiB).
|
||||||
"--loom=31",
|
"--loom=32",
|
||||||
]
|
]
|
||||||
volumes = [
|
volumes = [
|
||||||
"/data/services/urbit:/urbit",
|
"/data/services/urbit:/urbit",
|
||||||
@@ -45,7 +49,7 @@ job "urbit" {
|
|||||||
|
|
||||||
resources {
|
resources {
|
||||||
# dependent on --loom setting + some buffer
|
# dependent on --loom setting + some buffer
|
||||||
memory = 2100
|
memory = 4200
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
48
services/wavelog.hcl
Normal file
48
services/wavelog.hcl
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# ABOUTME: Nomad job spec for Wavelog amateur radio logging application
|
||||||
|
# ABOUTME: Uses MySQL database and OIDC auth via Traefik middleware
|
||||||
|
|
||||||
|
job "wavelog" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
group "wavelog" {
|
||||||
|
network {
|
||||||
|
port "http" {
|
||||||
|
to = 80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "wavelog" {
|
||||||
|
driver = "docker"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "ghcr.io/wavelog/wavelog:latest"
|
||||||
|
ports = ["http"]
|
||||||
|
volumes = [
|
||||||
|
"/data/services/wavelog/config:/var/www/html/application/config/docker",
|
||||||
|
"/data/services/wavelog/uploads:/var/www/html/uploads",
|
||||||
|
"/data/services/wavelog/userdata:/var/www/html/userdata",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
CI_ENV = "docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
cpu = 300
|
||||||
|
memory = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "wavelog"
|
||||||
|
port = "http"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.wavelog.entryPoints=websecure",
|
||||||
|
"traefik.http.routers.wavelog.middlewares=oidc-auth@file",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ job "weewx" {
|
|||||||
driver = "docker"
|
driver = "docker"
|
||||||
|
|
||||||
config {
|
config {
|
||||||
image = "gitea.v.paler.net/ppetru/weewx:latest"
|
image = "gitea.v.paler.net/alo/weewx:latest"
|
||||||
# to be able to receive UDP broadcast packets from the weatherlink
|
# to be able to receive UDP broadcast packets from the weatherlink
|
||||||
network_mode = "host"
|
network_mode = "host"
|
||||||
volumes = [
|
volumes = [
|
||||||
@@ -29,7 +29,7 @@ job "weewx" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
memory = 1024
|
memory = 1500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ job "weewx" {
|
|||||||
driver = "docker"
|
driver = "docker"
|
||||||
|
|
||||||
config {
|
config {
|
||||||
image = "gitea.v.paler.net/ppetru/opensprinkler-weather:latest"
|
image = "gitea.v.paler.net/alo/opensprinkler-weather:latest"
|
||||||
|
|
||||||
ports = [ "osweather" ]
|
ports = [ "osweather" ]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ job "wordpress" {
|
|||||||
user = "237"
|
user = "237"
|
||||||
|
|
||||||
config {
|
config {
|
||||||
image = "gitea.v.paler.net/ppetru/wordpress"
|
image = "gitea.v.paler.net/alo/wordpress"
|
||||||
ports = ["http"]
|
ports = ["http"]
|
||||||
volumes = [
|
volumes = [
|
||||||
"/data/services/wordpress:/var/www/html",
|
"/data/services/wordpress:/var/www/html",
|
||||||
|
|||||||
Reference in New Issue
Block a user