name: Deploy Nix Site description: Deploy static site built with Nix flake to S3 and Nomad inputs: site-name: description: 'Site identifier (used as service name in Nomad)' required: true traefik-rule: description: 'Traefik routing rule (e.g., Host(`example.com`) or Host(`example.com`) || Host(`www.example.com`))' required: true flake-output: description: 'Nix flake output to build (e.g., .#packages.x86_64-linux.default or .#)' required: false default: '.#' s3-endpoint: description: 'S3 endpoint' required: false default: 'https://s3.toph.so' runs: using: composite steps: - name: Install tools shell: bash run: | # Install AWS CLI nix profile install nixpkgs#awscli2 # Set Nomad connection echo "NOMAD_ADDR=http://alvin:4646" >> $GITHUB_ENV echo "NOMAD_TOKEN=${{ env.NOMAD_TOKEN }}" >> $GITHUB_ENV - name: Build site with Nix shell: bash run: | # Configure S3 as substituter to pull cached dependencies export AWS_ACCESS_KEY_ID="${{ env.S3_ACCESS_KEY }}" export AWS_SECRET_ACCESS_KEY="${{ env.S3_SECRET_KEY }}" # Build with S3 cache as substituter (fetches cached deps) nix build ${{ inputs.flake-output }} \ --print-build-logs \ --option substituters "https://cache.nixos.org s3://nix-cache?endpoint=${{ inputs.s3-endpoint }}&scheme=https" \ --option trusted-public-keys "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= $(cat /tmp/cache-pub-key.pem 2>/dev/null || echo '')" # Get the store path STORE_PATH=$(readlink -f result) STORE_HASH=$(basename "$STORE_PATH") echo "STORE_PATH=$STORE_PATH" >> $GITHUB_ENV echo "STORE_HASH=$STORE_HASH" >> $GITHUB_ENV echo "📦 Built: $STORE_PATH" - name: Push to binary cache shell: bash run: | # Configure S3 binary cache export AWS_ACCESS_KEY_ID="${{ env.S3_ACCESS_KEY }}" export AWS_SECRET_ACCESS_KEY="${{ env.S3_SECRET_KEY }}" # Write signing key to temporary file echo "${{ env.NIX_SIGNING_KEY }}" > /tmp/nix-signing-key.pem chmod 600 /tmp/nix-signing-key.pem # Push entire closure (derivation + all dependencies) to cache nix copy \ --to "s3://nix-cache?endpoint=${{ inputs.s3-endpoint }}&scheme=https&secret-key=/tmp/nix-signing-key.pem" \ --derivation \ "$STORE_PATH" # Clean up key file rm -f /tmp/nix-signing-key.pem echo "✅ Pushed to binary cache: $STORE_HASH (with all dependencies)" - name: Deploy via Nomad shell: bash run: | cat > /tmp/deploy-${{ inputs.site-name }}.nomad.json <<'NOMAD_EOF' { "Job": { "ID": "${{ inputs.site-name }}", "Name": "${{ inputs.site-name }}", "Namespace": "static-sites", "Type": "service", "Datacenters": ["contabo"], "Constraints": [{ "LTarget": "${node.unique.name}", "RTarget": "alvin", "Operand": "=" }], "TaskGroups": [{ "Name": "web", "Count": 1, "Networks": [{ "Mode": "bridge", "DynamicPorts": [{ "Label": "http", "To": 8080 }] }], "Services": [{ "Name": "${{ inputs.site-name }}", "PortLabel": "http", "Provider": "nomad", "Tags": [ "traefik.enable=true", "traefik.http.routers.${{ inputs.site-name }}.rule=${{ inputs.traefik-rule }}", "traefik.http.routers.${{ inputs.site-name }}.entrypoints=websecure", "traefik.http.routers.${{ inputs.site-name }}.tls.certresolver=letsencrypt" ] }], "Volumes": { "site-data": { "Type": "host", "Source": "site-data", "ReadOnly": false } }, "Tasks": [ { "Name": "fetch", "Driver": "docker", "Lifecycle": { "Hook": "prestart", "Sidecar": false }, "Config": { "image": "nixos/nix:latest", "command": "/bin/sh", "args": [ "-c", "nix copy --from 's3://nix-cache?endpoint=${{ inputs.s3-endpoint }}&scheme=https' '${STORE_PATH}' && cp -r ${STORE_PATH}/* /alloc/data/" ] }, "Env": { "AWS_ACCESS_KEY_ID": "${{ env.S3_ACCESS_KEY }}", "AWS_SECRET_ACCESS_KEY": "${{ env.S3_SECRET_KEY }}", "STORE_PATH": "${{ env.STORE_PATH }}" }, "VolumeMounts": [{ "Volume": "site-data", "Destination": "/alloc/data" }], "Resources": { "CPU": 200, "MemoryMB": 256 } }, { "Name": "server", "Driver": "docker", "Config": { "image": "joseluisq/static-web-server:2", "ports": ["http"] }, "Env": { "SERVER_ROOT": "/var/www", "SERVER_LOG_LEVEL": "info" }, "VolumeMounts": [{ "Volume": "site-data", "Destination": "/var/www", "ReadOnly": true }], "Resources": { "CPU": 100, "MemoryMB": 64 } } ] }] } } NOMAD_EOF nomad job run -json /tmp/deploy-${{ inputs.site-name }}.nomad.json - name: Deployment summary shell: bash run: | echo "✅ Deployed ${{ inputs.site-name }}" echo "📋 Traefik rule: ${{ inputs.traefik-rule }}" echo "📦 Store path: $STORE_PATH"