
Requirements
- You’ve created a Runpod account.
- You’ve created a Runpod API key.
- You’ve installed Python 3.10 to 3.14.
- You’ve completed the Flash quickstart or are familiar with Flash basics.
What you’ll build
By the end of this tutorial, you’ll have a working image generation application that:- Accepts text prompts as input.
- Generates photorealistic images using Stable Diffusion XL.
- Runs entirely on Runpod’s GPU infrastructure.
- Saves generated images to your local machine.
Step 1: Set up your project
Create a new directory for your project and set up a Python virtual environment:.env file with your API key. Flash loads .env from the current directory automatically:
YOUR_API_KEY with your API key from the Runpod console.
The directory name becomes your Flash app name. Run
flash deploy and your script from this directory.Step 2: Understand Stable Diffusion XL
Stable Diffusion XL (SDXL) is a text-to-image model from Stability AI. It generates 1024x1024 images and follows prompts more closely than Stable Diffusion 1.5. What to expect when you run it on Runpod:- Model size: about 7 GB of fp16 weights, downloaded by the worker the first time it loads the model.
- VRAM: at least 16 GB. This tutorial uses 24 GB GPUs.
- Speed: once the model is loaded, 30 steps take about 4-5 seconds on an RTX 4090.
Step 3: Create your project file
Create a file calledimage_generation.py in your project directory. The next three steps build it up section by section. The complete file is at the end of Step 6.
Step 4: Add imports and configuration
_pipeline caches the model on the worker. Without it, every request would load 7 GB of weights from disk into GPU memory again, which adds about 20 seconds per image.
Step 5: Define the image generation function
Add the endpoint function that runs on the GPU worker:name="image-generation": Identifies your endpoint in the Runpod console.gpu=[GpuGroup.ADA_24, GpuGroup.AMPERE_24]: Uses 24 GB GPUs such as the RTX 4090, L4, or A5000. SDXL needs at least 16 GB.workers=2: Allows up to 2 parallel workers.idle_timeout=900: Keeps a worker running for 15 minutes after its last request, so the loaded model stays in memory.dependencies: Installed into your build.torchis already in the worker image, so Flash leaves it out of the upload.
Code breakdown
Code breakdown
Imports inside the function: The worker imports your file once and keeps it in memory between requests, so module-level variables like Endpoint results are sent back as JSON, so the PNG is encoded as a base64 string.
torch and diffusers are imported inside generate_image because they’re installed on the worker, not on your machine.Model caching:_pipeline persist. The first request on a worker loads the model. Later requests on the same worker reuse it. model_cached in the result tells you which happened.Model loading parameters:torch_dtype=torch.float16: Half precision. Uses less VRAM and runs faster.use_safetensors=True: Loads the safetensors format.variant="fp16": Downloads the fp16 weights (about 7 GB instead of 14 GB).
prompt: What you want to see in the image.negative_prompt: What you don’t want, such as “blurry, low quality”.num_inference_steps: More steps give more refined images but take longer. 20-50 is typical.guidance_scale: How closely to follow the prompt. 7-10 is typical.generator: A seeded generator makes results reproducible. The same prompt and seed give the same image.
Step 6: Add the main function and image saving
Add a helper that saves images and amain() function that calls the endpoint. Everything in this step runs on your machine:
if __name__ == "__main__": block at the end of the file. Without it, the script exits without doing anything.
Complete file
image_generation.py
image_generation.py
Step 7: Deploy and run your first generation
Deploy the endpoint from your project directory:image-generation endpoint:
generated_images/sdxl_output.png to see your image.
Step 8: Generate several images in parallel
Replacemain() with a version that sends three prompts at once with asyncio.gather:
@Endpoint function, so you don’t need to redeploy. Run the script:
workers=2). A new worker loads the model on its first request, so that result shows model already loaded: False.
Keep the endpoint function and the code that calls it in the same file. If a second file imports
generate_image, flash deploy treats it as a second definition and fails with endpoint 'image-generation' is defined in multiple files.Understanding generation parameters
Let’s explore how different parameters affect image quality:Number of inference steps
- 15-20 steps: Faster, but fewer refined details.
- 30 steps: A good balance of quality and speed (about 4-5 seconds on an RTX 4090). Recommended.
- 50+ steps: Diminishing returns, with little visible improvement.
Guidance scale
- 3-5: More artistic freedom, less literal interpretation.
- 7-10: Balanced, follows prompt closely - recommended.
- 12+: Very literal, may produce oversaturated or exaggerated images.
Negative prompts
Negative prompts tell the model what to avoid:- Remove common artifacts (“distorted”, “low quality”).
- Avoid unwanted styles (“cartoon”, “3D render”).
- Fix common issues (“bad anatomy”, “extra fingers”).
Troubleshooting
Out of memory error
Issue:RuntimeError: CUDA out of memory.
Cause: SDXL requires significant VRAM (16GB minimum).
Solutions:
-
Verify you’re using 24GB GPUs:
-
Use half-precision (already in the example):
-
If still failing, use 48GB GPUs:
Model download fails
Issue:Error: Failed to download model from Hugging Face.
Solutions:
-
Increase execution timeout for first run:
- Check Hugging Face Hub status at status.huggingface.co.
- Check the worker logs for the endpoint in the Runpod console. Download errors from Hugging Face appear there.
Image quality is poor
Issue: Generated images look blurry or low quality. Solutions:-
Increase inference steps:
-
Adjust guidance scale:
-
Improve your prompt:
-
Add quality keywords to your prompt:
- “highly detailed”
- “sharp focus”
- “8k”
- “photorealistic”
- “professional”
Slow generation
Issue: Every request takes much longer than 10 seconds. Checkmodel_cached in the result. If it’s False on most requests, workers are loading the model again:
- Make sure
_pipelineis a module-level variable andgenerate_imagedeclaresglobal _pipeline. A pipeline created as a local variable is loaded again on every request. - Increase
idle_timeoutso workers stay up between requests: - Set
workers=(1, 2)to keep one worker running at all times. You pay for that worker while it’s idle.
model_cached is True and generation is still slow, reduce num_steps.
First run fails after 90 seconds
Issue: The first run fails with one of these errors:runpod-flash 1.21.0 waits up to 90 seconds for a result. The first request on a new worker can take longer: the worker may wait for a free GPU, then it starts and loads the model. The endpoint is deployed, and the request keeps running after the error.
Solution: Run the script again. The worker keeps starting after the error, so the next run is faster. Raising FLASH_SENTINEL_TIMEOUT above 90 doesn’t help in 1.21.0: the request returns at 90 seconds with the job still running, and the script fails with the KeyError instead.
To see whether a worker is up, check the endpoint’s health with its ID from flash deploy. curl reads your API key from the RUNPOD_API_KEY environment variable, which flash login doesn’t set, so export it first:
workers.throttled means the worker is waiting for a GPU. A non-zero workers.idle, workers.ready, or workers.running means a worker is up.
Build runs out of disk space
Issue:flash deploy fails with No space left on device or Disk quota exceeded while installing dependencies.
Cause: A dependency that requires torch, such as accelerate, makes the local build download torch and several GB of CUDA libraries on Linux.
Solution: Remove that dependency, or exclude it from the build:
flash deploy for --exclude and --no-deps.
Endpoint defined in multiple files
Issue:flash deploy fails with endpoint 'image-generation' is defined in multiple files.
Cause: Another .py file in the project directory imports generate_image. Flash scans every file and counts the import as a second definition.
Solution: Keep the endpoint function and the code that calls it in one file, or move the extra script out of the project directory.
Images look distorted or have artifacts
Issue: Generated images have weird artifacts or distortions. Solutions:-
Use negative prompts:
-
Adjust guidance scale (try 7-9 range):
-
Increase inference steps for better refinement:
Next steps
Now that you’ve built an image generation script with Flash, you can:Try other Stable Diffusion models
Explore different models from Hugging Face: SDXL Turbo uses the same pipeline and generates an image in 1-4 steps at 512x512. ChangeMODEL_ID at the top of the file:
flash deploy again so the workers pick up the new model. Then call it with few steps and no guidance in main():
StableDiffusionXLPipeline with AutoPipelineForText2Image, which picks the right class for the model, and redeploy.
Add image-to-image generation
Use an existing image as a starting point:Build a Flash app
Convert your script to a production Flash app:flash init creates gpu_worker.py, cpu_worker.py, and lb_worker.py at the project root. See Build Flash apps for how they fit together.
Optimize with network volumes
Each new worker downloads the 7 GB of SDXL weights again. A network volume keeps one copy that every worker reads, so new workers skip the download. Point the Hugging Face cache at the volume withHF_HOME:
/runpod-volume on every worker. A network volume lives in one data center, so workers only run there.
Explore advanced features
- LoRA fine-tuning: Customize SDXL for specific styles.
- ControlNet: Guide generation with edge maps, depth, or pose.
- Inpainting: Edit specific parts of images.
- Upscaling: Generate higher resolution images.