{"course":{"slug":"ai-images","title":"Image Generation, in Practice","url":"https://addaly.com/learn/ai-images","level":"beginner","track":"create"},"lesson":{"slug":"a-reproducible-setup","title":"The setup that still works in six months","url":"https://addaly.com/learn/ai-images/a-reproducible-setup","position":10,"of":69,"minutes":7,"answer":"Diffusion output depends on the exact version of the interface, the model hash and the sampler implementation.","keyIdea":"Diffusion output depends on the exact version of the interface, the model hash and the sampler implementation, so recording those four or five facts with each approved image is what makes a client revision six months later a ten-minute job."},"body":{"format":"markdown","text":"## The call you will get\n\nA client approves an image in March. In September they want the same scene with the bottle turned slightly. You open your folder, find the prompt, and generate. It is a different picture.\n\nNothing is broken. Between March and September you updated the interface, a sampler implementation changed a rounding detail, a node pack bumped its defaults, and the checkpoint you used was re-uploaded with the same name and different weights. Every one of these is common, and any one of them is enough.\n\n## The five facts that make an image reproducible\n\nEverything else is noise. These are the ones that actually matter:\n\n1. **Model hash**, not model name. Names are reused constantly. The hash is in the interface's model list and in the generation metadata.\n2. **Interface version**, as a git commit, not \"latest\".\n3. **Sampler and scheduler names, plus steps, CFG and seed.**\n4. **Every LoRA with its filename, hash and strength.**\n5. **Resolution and any upscaler used.**\n\nThe interface already writes most of this into the PNG metadata, which is a genuine gift and one most people throw away by exporting to JPEG for the client and deleting the original. Keep the PNG.\n\n## Pinning, concretely\n\nFor anything you are paid for, treat the setup as a dependency you control.\n\n```bash\n# in your interface folder\ngit rev-parse HEAD > ENVIRONMENT.txt          # exact commit\npip freeze >> ENVIRONMENT.txt                 # exact package versions\npython --version >> ENVIRONMENT.txt\n```\n\nThen, when a project is approved, copy `ENVIRONMENT.txt` into the project folder next to the approved PNGs. It is four lines of shell and it converts \"I cannot reproduce this\" into \"check out that commit\".\n\nDo not run `git pull` in the middle of a project. Update between projects, deliberately, and re-run a known image afterwards to see whether anything moved. This feels excessive until the first time it saves you.\n\n## A folder structure that survives\n\n```text\nclient-name/\n  2026-03-brief.md              # the spec, signed off\n  refs/                         # the reference pack\n  ENVIRONMENT.txt\n  models.txt                    # names + hashes of every model used\n  gen/\n    v01/                        # raw PNGs with metadata intact\n    v02/\n  work/                         # layered editor files\n  deliver/\n    hero-1200x1500-v03.jpg\n```\n\nThe two rules that make it work: raw generations never get edited in place, and the delivery folder only ever contains files you have actually sent.\n\n## The limit of all this\n\nReproducibility has a hard ceiling on hosted services, and it is worth being clear-eyed. When you call an API, the provider can update the model behind the same name, change a hidden prompt-rewriting step, or retire the version entirely. Several have done all three. Your seed and parameters are recorded faithfully and still produce a different picture, and there is nothing in your folder that can fix it.\n\nThis is the strongest practical argument for keeping open weights locally for work that may need revisiting: a checkpoint file on your disk cannot be updated out from under you. It is also an argument for a specific, cheap habit: when work is approved, archive the layered editor file, not just the prompt. A PSD or XCF with the generated plate on its own layer is reproducible by definition, because the pixels are in it.\n\nKeep the file. Prompts are notes; files are assets.\n\n## Back it up, because the disk is the single point of failure\n\nEverything above assumes the files still exist. A working image setup is unusual in that most of its bulk (checkpoints, LoRAs, control models) is re-downloadable and therefore not worth backing up, while a tiny fraction of it is irreplaceable.\n\nBack up the small part:\n\n- The raw generated PNGs with their metadata.\n- The layered editor files.\n- Workflow graphs, `ENVIRONMENT.txt`, `models.txt`, the brief.\n\nThat is usually a few hundred megabytes per project, which fits anywhere. Excluding the model folders from your backup is the difference between a five-minute sync and an overnight one, and it is the reason people stop backing up at all.\n\nOne exception is worth carving out. If a project depended on a community checkpoint or LoRA, archive that file too. Uploads are deleted, accounts close, and a model that a client's approved campaign depends on can stop existing. A 6 GB file is cheap insurance against a deliverable you cannot ever regenerate.\n\n## What \"reproducible\" honestly means here\n\nEven with everything recorded, exact bit-for-bit reproduction across different hardware is not guaranteed. Floating-point arithmetic is not perfectly associative, GPU kernels differ between vendors and driver versions, and two machines can follow the same seed to visually identical but numerically different images.\n\nFor working purposes that is fine: you want the same picture, not the same bytes. Be aware of it when somebody insists a seed produced a different result on their machine: they may be right, and neither of you has done anything wrong."},"check":{"question":"An approved image cannot be reproduced six months later despite identical prompt, seed, sampler, steps and CFG. Which recorded fact would most likely have explained it?","options":[{"text":"The random number generator differs between CPU and GPU seeding modes, which changes the initial noise even when the seed number is unchanged.","correct":false,"tempting":"A genuine source of variation, but it is a setting rather than a drift, and it would not change between two runs on the same unchanged machine."},{"text":"The model hash, because checkpoints are often re-uploaded under the same filename with different weights.","correct":true},{"text":"The original output resolution, because generating at a different size gives the sampler a differently shaped starting latent and therefore a different picture.","correct":false,"tempting":"Resolution does change the image completely, but it is part of the parameters already stated as identical in the question."},{"text":"The negative prompt, which a number of interfaces leave out of the saved metadata block unless the option is explicitly enabled.","correct":false,"tempting":"Most interfaces do save it, and like the other parameters it is text the operator still holds; the thing that silently changed is the file the name points to."}]},"prev":{"slug":"the-safety-filter-differs","title":"Every platform's filter is different, and that is a production risk","url":"https://addaly.com/learn/ai-images/the-safety-filter-differs"},"next":{"slug":"cost-and-batching","title":"You are paying to make decisions you could make cheaply","url":"https://addaly.com/learn/ai-images/cost-and-batching"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'The setup that still works in six months', in Image Generation, in Practice. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/ai-images/a-reproducible-setup (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"The setup that still works in six months","courseTitle":"Image Generation, in Practice","module":{"index":1,"slug":"the-kit","title":"The kit, and what each piece of it costs you"},"answer":null,"keyIdea":"Diffusion output depends on the exact version of the interface, the model hash and the sampler implementation, so recording those four or five facts with each approved image is what makes a client revision six months later a ten-minute job.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"the-call-you-will-get","runs":[{"text":"The call you will get"}]},{"t":"p","runs":[{"text":"A client approves an image in March. In September they want the same scene with the bottle turned slightly. You open your folder, find the prompt, and generate. It is a different picture."}]},{"t":"p","runs":[{"text":"Nothing is broken. Between March and September you updated the interface, a sampler implementation changed a rounding detail, a node pack bumped its defaults, and the checkpoint you used was re-uploaded with the same name and different weights. Every one of these is common, and any one of them is enough."}]},{"t":"h2","id":"the-five-facts-that-make-an-image-reproducible","runs":[{"text":"The five facts that make an image reproducible"}]},{"t":"p","runs":[{"text":"Everything else is noise. These are the ones that actually matter:"}]},{"t":"ol","start":1,"items":[[{"text":"Model hash","b":true},{"text":", not model name. Names are reused constantly. The hash is in the interface’s model list and in the generation metadata."}],[{"text":"Interface version","b":true},{"text":", as a git commit, not “latest”."}],[{"text":"Sampler and scheduler names, plus steps, CFG and seed.","b":true}],[{"text":"Every LoRA with its filename, hash and strength.","b":true}],[{"text":"Resolution and any upscaler used.","b":true}]]},{"t":"p","runs":[{"text":"The interface already writes most of this into the PNG metadata, which is a genuine gift and one most people throw away by exporting to JPEG for the client and deleting the original. Keep the PNG."}]},{"t":"h2","id":"pinning-concretely","runs":[{"text":"Pinning, concretely"}]},{"t":"p","runs":[{"text":"For anything you are paid for, treat the setup as a dependency you control."}]},{"t":"code","lang":"bash","label":"Bash","text":"# in your interface folder\ngit rev-parse HEAD > ENVIRONMENT.txt          # exact commit\npip freeze >> ENVIRONMENT.txt                 # exact package versions\npython --version >> ENVIRONMENT.txt"},{"t":"p","runs":[{"text":"Then, when a project is approved, copy "},{"text":"ENVIRONMENT.txt","code":true},{"text":" into the project folder next to the approved PNGs. It is four lines of shell and it converts “I cannot reproduce this” into “check out that commit”."}]},{"t":"p","runs":[{"text":"Do not run "},{"text":"git pull","code":true},{"text":" in the middle of a project. Update between projects, deliberately, and re-run a known image afterwards to see whether anything moved. This feels excessive until the first time it saves you."}]},{"t":"h2","id":"a-folder-structure-that-survives","runs":[{"text":"A folder structure that survives"}]},{"t":"code","lang":"text","label":"Text","text":"client-name/\n  2026-03-brief.md              # the spec, signed off\n  refs/                         # the reference pack\n  ENVIRONMENT.txt\n  models.txt                    # names + hashes of every model used\n  gen/\n    v01/                        # raw PNGs with metadata intact\n    v02/\n  work/                         # layered editor files\n  deliver/\n    hero-1200x1500-v03.jpg"},{"t":"p","runs":[{"text":"The two rules that make it work: raw generations never get edited in place, and the delivery folder only ever contains files you have actually sent."}]},{"t":"h2","id":"the-limit-of-all-this","runs":[{"text":"The limit of all this"}],"role":"limit"},{"t":"p","runs":[{"text":"Reproducibility has a hard ceiling on hosted services, and it is worth being clear-eyed. When you call an API, the provider can update the model behind the same name, change a hidden prompt-rewriting step, or retire the version entirely. Several have done all three. Your seed and parameters are recorded faithfully and still produce a different picture, and there is nothing in your folder that can fix it."}]},{"t":"p","runs":[{"text":"This is the strongest practical argument for keeping open weights locally for work that may need revisiting: a checkpoint file on your disk cannot be updated out from under you. It is also an argument for a specific, cheap habit: when work is approved, archive the layered editor file, not just the prompt. A PSD or XCF with the generated plate on its own layer is reproducible by definition, because the pixels are in it."}]},{"t":"p","runs":[{"text":"Keep the file. Prompts are notes; files are assets."}]},{"t":"h2","id":"back-it-up-because-the-disk-is-the-single-point-of-failure","runs":[{"text":"Back it up, because the disk is the single point of failure"}]},{"t":"p","runs":[{"text":"Everything above assumes the files still exist. A working image setup is unusual in that most of its bulk (checkpoints, LoRAs, control models) is re-downloadable and therefore not worth backing up, while a tiny fraction of it is irreplaceable."}]},{"t":"p","runs":[{"text":"Back up the small part:"}]},{"t":"ul","items":[[{"text":"The raw generated PNGs with their metadata."}],[{"text":"The layered editor files."}],[{"text":"Workflow graphs, "},{"text":"ENVIRONMENT.txt","code":true},{"text":", "},{"text":"models.txt","code":true},{"text":", the brief."}]]},{"t":"p","runs":[{"text":"That is usually a few hundred megabytes per project, which fits anywhere. Excluding the model folders from your backup is the difference between a five-minute sync and an overnight one, and it is the reason people stop backing up at all."}]},{"t":"p","runs":[{"text":"One exception is worth carving out. If a project depended on a community checkpoint or LoRA, archive that file too. Uploads are deleted, accounts close, and a model that a client’s approved campaign depends on can stop existing. A 6 GB file is cheap insurance against a deliverable you cannot ever regenerate."}]},{"t":"h2","id":"what-reproducible-honestly-means-here","runs":[{"text":"What “reproducible” honestly means here"}]},{"t":"p","runs":[{"text":"Even with everything recorded, exact bit-for-bit reproduction across different hardware is not guaranteed. Floating-point arithmetic is not perfectly associative, GPU kernels differ between vendors and driver versions, and two machines can follow the same seed to visually identical but numerically different images."}]},{"t":"p","runs":[{"text":"For working purposes that is fine: you want the same picture, not the same bytes. Be aware of it when somebody insists a seed produced a different result on their machine: they may be right, and neither of you has done anything wrong."}]}],"check":{"question":[{"text":"An approved image cannot be reproduced six months later despite identical prompt, seed, sampler, steps and CFG. Which recorded fact would most likely have explained it?"}],"options":[{"text":[{"text":"The random number generator differs between CPU and GPU seeding modes, which changes the initial noise even when the seed number is unchanged."}],"correct":false,"tempting":[{"text":"A genuine source of variation, but it is a setting rather than a drift, and it would not change between two runs on the same unchanged machine."}]},{"text":[{"text":"The model hash, because checkpoints are often re-uploaded under the same filename with different weights."}],"correct":true,"tempting":null},{"text":[{"text":"The original output resolution, because generating at a different size gives the sampler a differently shaped starting latent and therefore a different picture."}],"correct":false,"tempting":[{"text":"Resolution does change the image completely, but it is part of the parameters already stated as identical in the question."}]},{"text":[{"text":"The negative prompt, which a number of interfaces leave out of the saved metadata block unless the option is explicitly enabled."}],"correct":false,"tempting":[{"text":"Most interfaces do save it, and like the other parameters it is text the operator still holds; the thing that silently changed is the file the name points to."}]}],"order":[3,1,2,0],"why":null},"prev":{"slug":"the-safety-filter-differs","title":"Every platform’s filter is different, and that is a production risk","minutes":7},"next":{"slug":"cost-and-batching","title":"You are paying to make decisions you could make cheaply","minutes":8}}}