Skip to main content
Flash is now part of the Runpod SDK. This page documents the standalone runpod-flash package, which stops getting fixes and updates after its deprecation date. For new projects, use the Runpod SDK. To see how Flash commands and imports map to the SDK, see Migrating from Flash.
This tutorial shows you how to build an image generation script using Flash and Stable Diffusion XL (SDXL). You’ll learn how to load a pretrained diffusion model on a GPU worker and generate images from text prompts.

Requirements

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:
Install Flash using uv:
Log in so Flash can deploy and call endpoints on your account:
Alternatively, create a .env file with your API key. Flash loads .env from the current directory automatically:
Replace 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.
You’ll use the diffusers library from Hugging Face to load and run the model.

Step 3: Create your project file

Create a file called image_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:
Configuration breakdown:
  • 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. torch is already in the worker image, so Flash leaves it out of the upload.
Don’t add accelerate to dependencies. It requires torch, so on Linux flash deploy downloads torch and several GB of CUDA libraries into the local build, which can fill your disk. Without it, the build is about 50 MB.
Imports inside the function: torch and diffusers are imported inside generate_image because they’re installed on the worker, not on your machine.Model caching:
The worker imports your file once and keeps it in memory between requests, so module-level variables like _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).
Generation parameters:
  • 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.
Image encoding:
Endpoint results are sent back as JSON, so the PNG is encoded as a base64 string.

Step 6: Add the main function and image saving

Add a helper that saves images and a main() function that calls the endpoint. Everything in this step runs on your machine:
Keep the if __name__ == "__main__": block at the end of the file. Without it, the script exits without doing anything.

Complete file

Step 7: Deploy and run your first generation

Deploy the endpoint from your project directory:
Flash installs your dependencies into a local build directory, uploads the build, and creates the image-generation endpoint:
Then run the script from the same directory:
The first run takes about 80 seconds. The worker starts, downloads the SDXL weights, and loads them onto the GPU:
Run it again while the worker is warm. The model is already loaded, so the whole run takes about 7 seconds:
Open generated_images/sdxl_output.png to see your image.
The first request on a new worker can take longer than the 90 seconds runpod-flash 1.21.0 waits for a result, especially if the worker has to wait for a free GPU. If the script fails with timed out after 90s. the endpoint may not be deployed or the worker is still starting or KeyError: 'image_base64', the endpoint is deployed and the worker is still loading the model. Run the script again. See First run fails after 90 seconds.

Step 8: Generate several images in parallel

Replace main() with a version that sends three prompts at once with asyncio.gather:
You only changed code outside the @Endpoint function, so you don’t need to redeploy. Run the script:
All three requests are queued at once. A warm worker generates them one after another, about 18 seconds in total. If the queue builds up, Flash starts the second worker (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

Effects:
  • 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.
Generation time grows roughly in proportion to the number of steps.

Guidance scale

Effects:
  • 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:
Use negative prompts to:
  • 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:
  1. Verify you’re using 24GB GPUs:
  2. Use half-precision (already in the example):
  3. If still failing, use 48GB GPUs:

Model download fails

Issue: Error: Failed to download model from Hugging Face. Solutions:
  1. Increase execution timeout for first run:
  2. Check Hugging Face Hub status at status.huggingface.co.
  3. 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:
  1. Increase inference steps:
  2. Adjust guidance scale:
  3. Improve your prompt:
  4. Add quality keywords to your prompt:
    • “highly detailed”
    • “sharp focus”
    • “8k”
    • “photorealistic”
    • “professional”

Slow generation

Issue: Every request takes much longer than 10 seconds. Check model_cached in the result. If it’s False on most requests, workers are loading the model again:
  1. Make sure _pipeline is a module-level variable and generate_image declares global _pipeline. A pipeline created as a local variable is loaded again on every request.
  2. Increase idle_timeout so workers stay up between requests:
  3. Set workers=(1, 2) to keep one worker running at all times. You pay for that worker while it’s idle.
If 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:
Cause: 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:
A non-zero 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:
See 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:
  1. Use negative prompts:
  2. Adjust guidance scale (try 7-9 range):
  3. 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. Change MODEL_ID at the top of the file:
Run flash deploy again so the workers pick up the new model. Then call it with few steps and no guidance in main():
Models outside the SDXL family, such as Stable Diffusion 1.5, need a different pipeline class. Replace 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 with HF_HOME:
The volume is mounted at /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.
Last modified on October 27, 2026