Transfer¶
NARs, logs, build progress and credentials moving between worker and server. Workers always compress NARs with zstd. The server will never re-compress them.
Upload¶
sequenceDiagram
participant W as Worker
participant S as Server
W->>S: CacheQuery { mode: Push }
S->>W: CacheStatus (cached or not)
W->>S: UploadRequest { request_id, object, size }
S->>W: UploadGrant { Skip / Passthrough / Put / Multipart }
W->>S: UploadChunk ... (Passthrough only)
W->>S: UploadFinished { metadata }
S->>W: UploadCommitted { Ok / Retry / Rejected }
| Grant | When | Transfer |
|---|---|---|
Skip |
The path is already stored, or the server does not want the evaluation cache blob | Nothing |
Passthrough { resume_offset } |
Local NAR storage | UploadChunk frames of worker.nar.chunkBytes into <baseDir>/nar-partial, resuming after a break |
Put { url } |
S3, NAR up to 1 GiB | One presigned PUT, valid 1 h |
Multipart |
S3, NAR over 1 GiB | Presigned parts of at least 64 MiB |
- Admission is server-wide and fair across sessions.
- The limits are
upload.concurrency(8) large uploads andupload.bytesBudget(8 GiB) at once. - Small uploads (at most 1 MiB of NAR,
SMALL_UPLOAD_BYTES) get a window of their own,SMALL_UPLOADS_IN_FLIGHT(128). - The cost of a small upload is its two round trips. Each
EvalResultbatch of an evaluation will wait on the push of its own.drvfiles. - Small uploads go ahead of larger ones in their session. The server will turn first to sessions with a waiting small upload.
- Count and bytes of a permit come back after the object landed in storage, ahead of the graph record.
- Requests for a path coalesce across workers, REST and SSH. Followers get
Skipafter the graph recorded the first upload. - Followers take over the path after a failed first upload.
- The limits are
- Worker Side:
worker.nar.maxConcurrentUploads(16) slots for large uploads and 128 for small ones.- One job can hold at most half of the large slots.
- A slot is busy from the request until sending
UploadFinished, not through the commit. - The graph writer will receive commits in bursts and batch them.
- The worker will retry a
Retryup to 3 times. ARejectedanswer will fail the upload. - Both rules also hold for an answer arriving before the grant.
- A failed transfer or an abandoned request will send an
UploadCancelmessage.
- Commit: The server will check a passthrough upload by length and SHA-256, then move the upload into the NAR store.
- The commit of a presigned upload will complete the multipart and check the object size.
- The full digest check will happen only with
nar.verifyDigestset.
- Leases: A passthrough upload will expire after
upload.leaseIdleSecs(300 s) without progress. A failed job or closed session will release its uploads. - Evaluation cache blobs use the same handshake with
UploadObject::EvalCache.
Download for a Build¶
Workers prefetch every input missing from the local store ahead of the build.
CacheQuery { mode: Pull }for the missing paths. An uncached path will fail the build with the failure kindInputsUnavailableset.- Paths with a presigned
urldownload directly, 8 in parallel, with 4 attempts. A download will fail after 30 s without a byte, never on total length. - The rest go through
NarRequest. The server will answer each path withNarStreamHeader, thenNarPushframes ofnar.chunkBytes, or with aNarUnavailable/NarAbortmessage. - A broken stream will resume with
NarRequestResume { received_bytes, stream_token }from the<baseDir>/nar-partialdirectory. -
Workers import the NARs into the Nix store in dependency order.
-
Presigned downloads and
NarPushstreams both stage the compressed NAR under<baseDir>/nar-partial. - Jobs on the same worker share the transfer of a path, presigned or streamed. Later jobs read the same staged file.
- The staged file will go away with its last job, also after a failed or aborted job.
- Jobs waiting on a failed transfer will fail with it.
- A NAR failing the size and hash check will leave the shared transfers. The next job will download it again.
- Workers decompress each NAR from its staged file as a stream, first for the size and hash check, then into the Nix daemon.
- Workers skip the import of a path another job imported meanwhile.
| Condition | Delivered As |
|---|---|
S3 store, confirmed NAR above nar.smallBytes (1 MiB) |
Presigned GET URL |
| Anything else | Stream over /proto, at most nar.maxConcurrentServes (8) paths per connection and nar.maxConcurrentDownloads (16) across all connections |
- Small NARs come from an in-memory hot cache,
nar.hotCacheBytes(512 MiB). - A
NarUnavailableanswer will also remove the stale cache row on the server. - A storage error or timeout will answer with
NarAbortand leave the row and object in place. - A substitute build may get an upstream URL through a single-path
Pullquery withexternalset.
Logs¶
- The
LogChunk { job_id, task_index, data }messages stream build output on the bulk lane, without acknowledgement. - The server will append each chunk to the open attempt of the build at
task_index. - The server will drop chunks for evaluations or finished attempts.
- The worker will limit each build's log to
worker.log.burstBytesPerMin(8 MiB) andworker.log.sustainedBytesPerHour(64 MiB). - The worker will then stop the forward and add a truncation note.
- A build already in the store can forward its stored Nix log with
worker.log.fetchFromStore(on by default). - The log will end up as zstd chunks of
log.chunkBytes(256 KiB) with a chunk index after the build.
Build Progress¶
- The
BuildProgressmessage will report the transfers of one build in three phases, each with bytes and paths done and total.
| Phase | Transfer | Bytes |
|---|---|---|
Prefetch |
Missing inputs pulled from the Gradient cache before the build | Compressed NAR files, totals growing with each closure round |
Download |
A substitute or builtin:fetchurl download |
Compressed NAR file or the downloaded file |
Upload |
The build's own uncached outputs at the end of the job | NAR bytes read from the store |
- Workers report at most once a second on a change, plus once at the end of a phase.
bytes_totalisNonewhen one size is unknown.paths_totalisNonewhile a prefetch is still discovering paths. The last report of a phase will always carry the total.- Retried or failed transfers count only once.
- The server will keep the latest value per build for 15 s in memory.
- The build response will show the value only while the build has the
Buildingstatus. - The build will stay
Buildinguntil its job finished the upload. - The server will also publish
BuildProgressevents to the live endpoints. - Eval workers report fetch rows or live thunks through
EvalProgressat most once a second on a change. - An unchanged value is sent again once 30 s have passed.
- The server will keep
EvalProgressvalues the same way for 60 s, shown only during the fetch and evaluation steps. - The server will publish
EvalProgressasevaluation.activityevents to the evaluation and task live endpoints.
Credentials¶
- The only credential is the project's SSH key, for cloning private repositories and inputs.
- The server will decrypt the key and send
Credential { SshKey }right before theAssignJobmessage. - The credential will go only to a flake job with a fetch step on a
fetch-capable worker. - Projects without a key get no credential.
- Workers keep the key in locked memory, zeroed on drop.
- Workers clear the key at job completion.
Memory¶
- A
Putupload and a presigned download each hold the whole compressed NAR of up to 1 GiB in memory. - Passthrough and multipart uploads from a store path stream through a packer and never hold the whole NAR.