ci-actions/deploy-static-site/nomad-job.nix
Christopher Mühl 19468d38d8
refactor: migrate from S3 to Attic binary cache
Replace low-level S3 operations with native Attic client for better
performance, simplicity, and proper Nix binary cache protocol support.

Changes:
- Replace 'nix copy' + S3 with 'attic push'
- Remove S3_ACCESS_KEY, S3_SECRET_KEY, NIX_SIGNING_KEY requirements
- Add ATTIC_TOKEN requirement (explicit per-repo security)
- Default to 'ci' cache instead of 'toph'
- Update Nomad fetch task to pull from Attic instead of S3
- Simplify push-nix-cache to single attic push command
- Update documentation with new security model

Security:
- ATTIC_TOKEN must be explicitly provided as Forgejo secret
- Prevents untrusted repos from pushing to cache
- Separate ci/toph caches for different trust levels

Benefits:
- Simpler: Single command instead of sign + copy + sync
- Faster: Native Attic protocol vs S3 object storage
- Safer: Explicit opt-in prevents unauthorized cache writes
- Standards-compliant: Proper Nix binary cache protocol

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-27 21:19:09 +01:00

86 lines
2.5 KiB
Nix

let
domain = builtins.getEnv "DOMAIN";
siteHash = builtins.getEnv "SITE_HASH";
serverImage = builtins.getEnv "SERVER_IMAGE";
datacenter = builtins.getEnv "DATACENTER";
s3Bucket = builtins.getEnv "S3_BUCKET";
jobId = "site-" + builtins.replaceStrings [ "." ] [ "-" ] domain;
startupCmd =
"mkdir -p /var/www && " +
"aws s3 cp s3://${s3Bucket}/sites/${domain}/${siteHash}.tar.gz - " +
"| tar xz -C /var/www/ && " +
"exec static-web-server --port 8080 --root /var/www";
templateData =
"{{ with nomadVar \"nomad/jobs\" }}" +
"AWS_ACCESS_KEY_ID={{ .access_key }}\n" +
"AWS_SECRET_ACCESS_KEY={{ .secret_key }}\n" +
"AWS_ENDPOINT_URL={{ .endpoint }}\n" +
"{{ end }}";
job = {
Job = {
ID = jobId;
Name = jobId;
Namespace = "static-sites";
Type = "service";
Datacenters = [ datacenter ];
Update = {
MinHealthyTime = 5000000000;
HealthyDeadline = 60000000000;
MaxParallel = 1;
};
TaskGroups = [
{
Name = "site";
Count = 1;
Networks = [
{ DynamicPorts = [ { Label = "http"; To = 8080; } ]; }
];
Services = [
{
Name = jobId;
Provider = "nomad";
PortLabel = "http";
Tags = [
"traefik.enable=true"
"traefik.http.routers.${jobId}.rule=Host(`${domain}`)"
"traefik.http.routers.${jobId}.entrypoints=websecure"
"traefik.http.routers.${jobId}.tls.certresolver=letsencrypt"
];
Checks = [
{ Type = "http"; Path = "/"; Interval = 5000000000; Timeout = 5000000000; }
];
}
];
Tasks = [
{
Name = "server";
Driver = "docker";
Config = {
image = serverImage;
command = "/bin/bash";
args = [ "-c" startupCmd ];
ports = [ "http" ];
};
Templates = [
{
EmbeddedTmpl = templateData;
DestPath = "secrets/s3.env";
Envvars = true;
}
];
Resources = {
CPU = 100;
MemoryMB = 128;
};
}
];
}
];
};
};
in
builtins.toJSON job