Blog Post

Apps on Azure Blog
9 MIN READ

Serverless GPU Tutorial: Build an AI Image Generator with Azure Functions on Container Apps

Nitesh_Jain's avatar
Nitesh_Jain
Icon for Microsoft rankMicrosoft
Feb 04, 2026

Ever wanted to create your own AI-powered image generator? In this tutorial, we'll show you how to build one using Azure Functions running on Azure Container Apps with serverless GPUs. The best part? You don't need to worry about managing servers or installing GPU drivers—Azure handles all of that for you!

We're going to create an API that turns text descriptions into images using Stable Diffusion. Send it a prompt like "a cute robot painting a sunset" and get back a unique AI-generated image!

What We're Building

Here's what the final result looks like:

POST /api/generate
{
  "prompt": "A friendly robot chef cooking pasta in a cozy kitchen"
}

Response: { 
  "success": true, 
  "image": "base64-encoded-image..." 
}

The magic happens inside an Azure Function running on Azure Container Apps with GPU access. When a request comes in:

  1. The function receives your text prompt
  2. Stable Diffusion (running on a Tesla T4 GPU) generates the image
  3. You get back a base64-encoded PNG image

The simple flow: Your App → Azure Function on Container Apps → Stable Diffusion (on GPU) → Image!

Why Azure Functions + GPUs?

  • 🚀 Fast - NVIDIA T4 GPUs generate images in seconds
  • 💰 Cost-effective - Only pay when generating images (scales to zero!)
  • 🔧 Simple - No GPU drivers or infrastructure to manage
  • 📈 Scalable - Handles multiple requests automatically

Before You Start

You'll need a few things ready:

What You NeedWhy
Azure accountCreate a free account if you don't have one
GPU accessGPUs require special quota approval. Request access here - it usually takes a day or two
Azure CLIDownload here - this is how we'll deploy everything

💡 Tip: Request GPU access first since it takes time to approve. You can read through this tutorial while waiting!

Step 1: Get the Code

First, grab the sample code from GitHub:

git clone https://github.com/Azure-Samples/function-on-aca-gpu.git
cd function-on-aca-gpu

Here's what's in the project:

gpu-function-image-gen/
├── function_app.py      # The main code - handles requests and generates images
├── requirements.txt     # Python packages we need
├── Dockerfile          # Packages everything into a container
├── host.json           # Azure Functions settings
└── deploy.ps1          # One-click deployment script!

Step 2: Understand the Code

Let's look at the key parts. Don't worry—it's simpler than it looks!

The image generation function (function_app.py):

.route(route="generate", methods=["POST"])
def generate_image(req: func.HttpRequest) -> func.HttpResponse:
    # Get the prompt from the request
    req_body = req.get_json()
    prompt = req_body.get('prompt', '')
    
    # Load our AI model (only happens once, then it's cached)
    pipe = get_pipeline()
    
    # Generate the image - this is where the GPU magic happens!
    result = pipe(prompt=prompt, num_inference_steps=25)
    
    # Convert to base64 and send back
    image = result.images[0]
    # ... encoding logic ...
    
    return func.HttpResponse(json.dumps({"success": True, "image": img_base64}))

That's it! The heavy lifting is done by the diffusers library and the GPU.

Step 3: Deploy to Azure

You have three options for deployment:

OptionMethodBest For
Option AAzure Developer CLI (azd up)Fastest, one-command deployment
Option BPowerShell/Bash scriptsMore control, customizable
Option CAzure PortalLearning, visual approach

Option A: Deploy using Azure Developer CLI (azd up) 🚀

This is the fastest and recommended way to deploy! One command does everything.

Prerequisites:

Deploy:

# Clone the repo
git clone https://github.com/Azure-Samples/function-on-aca-gpu.git
cd function-on-aca-gpu

# Deploy everything with one command!
azd up

You'll be prompted for:

  • Environment name: A unique name for your deployment (e.g., gpufunc-dev)
  • Azure location: Select swedencentral (has GPU quota)
  • Azure subscription: Select your subscription

☕ Grab a coffee - this takes about 15-20 minutes. The azd up command will:

  1. Create a resource group (rg-{environmentName})
  2. Set up Log Analytics and Application Insights for monitoring
  3. Create a Container Registry and build your image in the cloud
  4. Create a Storage Account for Azure Functions
  5. Create a Container Apps Environment with GPU workload profile
  6. Deploy your Function App with GPU access
  7. Give you the URL when it's done!

When it finishes, you'll see:

Deploying services (azd deploy)

  (✓) Done: Deploying service api
  - Endpoint: https://ca-{envname}.{random}.swedencentral.azurecontainerapps.io/

SUCCESS: Your up workflow to provision and deploy to Azure completed in 20 minutes.

Resources Created:

ResourceName
Resource Grouprg-{environmentName}
Log Analyticslog-{environmentName}
Application Insightsappi-{environmentName}
Container Registryacr{environmentName}
Storage Accountst{environmentName}
Container Apps Environmentcae-{environmentName}
Function Appca-{environmentName}

Clean up when done:

azd down

Option B: Deploy Using Command Line 💻

Use our deployment scripts for more control over the deployment process.

On Windows (PowerShell):

cd function-on-aca-gpu
.\deploy.ps1

On Mac/Linux:

cd function-on-aca-gpu
chmod +x deploy.sh
./deploy.sh

☕ Grab a coffee - this takes about 10-15 minutes. The script will:

  1. Create a resource group for all our resources
  2. Set up a container registry to store our Docker image
  3. Build and upload the Docker image (no Docker Desktop needed!)
  4. Create a Container Apps environment with GPU support
  5. Deploy the function app
  6. Give you the URL when it's done!

When it finishes, you'll see something like:

============================================
🎉 Deployment Complete!
============================================
Function App URL: https://gpu-image-gen-func.jollybay-xxx.swedencentral.azurecontainerapps.io

Endpoints:
  - Generate: https://gpu-image-gen-func.../api/generate
  - Health: https://gpu-image-gen-func.../api/health
============================================

Option C: Deploy Using Azure Portal 🖱

If you prefer clicking through a UI, follow these steps:

Part 1: Create a Container Registry

  1. Go to the Azure Portal and search for Container Registries
  2. Click + Create
  3. Fill in the details:
SettingValue
SubscriptionSelect your subscription
Resource groupCreate new → gpu-functions-rg
Registry namegpufunctionsacr (must be globally unique)
LocationSweden Central
SKUStandard

Click Review + create → Create

💡 Note: We'll use system-assigned managed identity for authentication, so no need to enable admin user or save credentials!

Part 2: Build and Push the Docker Image

Since we can't build Docker images directly in the portal, use Azure Cloud Shell:

  1. Click the Cloud Shell icon (>_) in the top navigation bar
  2. Choose Bash
  3. Navigate to your cloned repo folder (from Step 1) and run:
cd function-on-aca-gpu

# Build and push to your registry
az acr build --registry gpufunctionsacr --image gpu-image-gen:latest --file Dockerfile .

Part 3: Create a Container Apps Environment with GPU

  1. Search for Container Apps Environments and click + Create
  2. Fill in the Basics tab:
SettingValue
SubscriptionSelect your subscription
Resource groupgpu-functions-rg
Environment namegpu-functions-env
RegionSweden Central
Environment typeWorkload profiles
  1. Click Workload profiles tab → + Add workload profile
  2. Configure the GPU profile:
SettingValue
Workload profile namegpu-profile
Workload profile sizeConsumption - GPU NC8as-T4
  1. Click AddReview + createCreate

Part 4: Create the Container App with Azure Functions

  1. Search for Container Apps and click + CreateContainer App
  2. Fill in the Basics tab:
SettingValue
SubscriptionSelect your subscription
Resource groupgpu-functions-rg
Container app namegpu-image-gen-func
Optimize for Azure Functions✅ Check this box! (This is important - it enables Azure Functions support)
RegionSweden Central
Container Apps EnvironmentSelect gpu-functions-env
  1. Click Next: Container > and fill in:
SettingValue
Use quickstart image❌ Uncheck
Namegpu-image-gen-container
Image sourceAzure Container Registry
Registrygpufunctionsacr
Imagegpu-image-gen
Image taglatest
Workload profilegpu-profile
GPU✅ Check this box
Managed Identity'System assigned'
  1. Add environment variables at the bottom of the Container tab:
NameValue
MODEL_IDrunwayml/stable-diffusion-v1-5
FUNCTIONS_WORKER_RUNTIMEpython
  1. Click Next: Ingress > and configure:
SettingValue
Ingress✅ Enabled
Ingress trafficAccepting traffic from anywhere
Target port80
  1. Click Review + createCreate

Part 5: Grant ACR Pull Permission to the Container App

After the Container App is created, we need to grant it permission to pull images from the registry:

  1. Go to your Container App (gpu-image-gen-func) → Identity (under Settings)
  2. Verify System assigned is On and note the Object ID
  3. Go to your Container Registry (gpufunctionsacr) → Access control (IAM)
  4. Click + Add → Add role assignment
  5. Select AcrPull role → Click Next
  6. Select Managed identity → Click + Select members
  7. Choose Container App and select gpu-image-gen-func
  8. Click Select → Review + assign → Review + assign

💡 Tip: The first deployment may fail because the identity wasn't ready yet. Simply restart the Container App or create a new revision after granting the role.

🎉 Done! Once deployed, find your function URL under Overview → Application Url

Test Your Image Generator! 🧪

Now that your function is deployed, let's make sure everything works!

Check the Health Endpoint

First, verify the GPU is available:

Invoke-RestMethod -Uri "https://YOUR-FUNCTION-URL/api/health"

You should see:

{
  "status": "healthy",
  "gpu_available": true,
  "gpu_info": {
    "name": "Tesla T4",
    "memory_total_gb": 15.56
  }
}

🎉 GPU detected! Now let's generate an image.

Generate Your First Image

# Create the request
$body = @{
    prompt = "A happy corgi astronaut floating in space, digital art"
    num_steps = 25
} | ConvertTo-Json

# Call the API
$response = Invoke-RestMethod -Uri "https://YOUR-FUNCTION-URL/api/generate" `
    -Method POST `
    -ContentType "application/json" `
    -Body $body

# Save the image to a file
$imageBytes = [Convert]::FromBase64String($response.image)
[IO.File]::WriteAllBytes("corgi-astronaut.png", $imageBytes)

# Open it!
Start-Process "corgi-astronaut.png"

⏱️ Note: First request is slow (1-2 minutes) because it downloads the AI model (~5GB). After that, images generate in just a few seconds!

Customize Your Prompts 🎨

Play around with different prompts! Here are some ideas:

PromptWhat You Get
"A cozy cabin in a snowy forest, warm lighting"Peaceful winter scene
"Cyberpunk city at night with neon signs"Futuristic cityscape
"Watercolor painting of a cat reading a book"Artistic cat portrait
"Steampunk robot serving tea, detailed"Victorian-style robot

Pro tips for better results:

  • Be specific: "golden retriever" works better than just "dog"
  • Add style hints: "digital art", "oil painting", "photograph"
  • Describe lighting: "sunset", "dramatic lighting", "soft glow"

API Reference

Here's everything you can send to the /api/generate endpoint:

ParameterTypeDefaultWhat It Does
promptstringrequiredDescribe what you want to see
negative_promptstring""What to avoid (e.g., "blurry, ugly")
num_stepsint25More steps = better quality but slower
guidance_scalefloat7.5Higher = follows prompt more strictly
widthint512Image width (keep at 512 for best results)
heightint512Image height

Example with all options:

{
  "prompt": "A magical library with floating books",
  "negative_prompt": "blurry, low quality, distorted",
  "num_steps": 30,
  "guidance_scale": 8.0,
  "width": 512,
  "height": 512
}

Making It Faster ⚡

The first request is slow because of "cold start"—the container needs to start up and load the AI model. Here's how to speed things up:

Option 1: Keep One Instance Warm

Tell Azure to always keep one container running:

az functionapp config set \
  --name gpu-image-gen-func \
  --resource-group gpu-functions-rg \
  --min-replicas 1

⚠️ Heads up: This keeps the GPU running 24/7, which costs more. Great for production, but maybe not for testing.

Option 2: Enable Artifact Streaming

This makes the container start faster:

az acr artifact-streaming update \
  --name gpufunctionsacr \
  --repository gpu-image-gen \
  --enable

What About Costs? 💰

This can be very affordable! Here's where to find pricing for each resource:

ResourcePricing Info
Azure Container Apps (GPU)Container Apps Pricing - GPU workload profiles section
Azure Container RegistryACR Pricing
Azure FunctionsFunctions Pricing - Container Apps hosting

Key cost-saving tips:

  • 💡 Set min-replicas to 0 during development - you only pay when the function is running!
  • 💡 GPU billing is per-second when containers are active
  • 💡 Scale to zero means $0 when idle (just wait for cold starts)
  • 💡 Use the Azure Pricing Calculator to estimate your monthly costs

Troubleshooting 🔧

"Model failed to load" error?

Some models require a Hugging Face account. We use runwayml/stable-diffusion-v1-5 which works without authentication.

Images look weird?

  • Try adding "blurry, distorted, low quality" to negative_prompt
  • Increase num_steps to 30 or 40

Function times out?

  • First request can take 2+ minutes. Be patient!
  • Check GPU is available with the /api/health endpoint

GPU not detected?

  • Make sure GPU quota was approved
  • Verify gpu-profile workload profile was created

What's Next? 🚀

Now that you have a working image generator, here are some ideas:

  • Build a web UI - Create a simple HTML page to call your API
  • Try different models - Swap to SDXL for higher quality images
  • Add image-to-image - Modify existing images with AI
  • Create a Discord bot - Let your friends generate images!

Wrapping Up

You just built your own AI image generator! 🎉

Here's what we accomplished:

✅ Deployed an Azure Function with GPU support
✅ Set up Stable Diffusion for image generation
✅ Created a simple API anyone can call
✅ Learned how to optimize costs and performance

The full source code is available at: github.com/Azure-Samples/function-on-aca-gpu

Have questions or built something cool? We'd love to hear about it!

Resource Clean-up

Don't want to keep paying? Delete everything with one command:

az group delete --name gpu-functions-rg --yes

This removes all the resources we created. You can always redeploy later!

Support and Feedback

The sample code shared in this blog post is provided as-is for learning and demonstration purposes. While Microsoft provides support for the underlying Azure infrastructure and services (Azure Container Apps, Azure Functions, Azure Container Registry), the sample application code is not a managed solution.

If you need assistance with Azure services, feel free to open a support request through the Microsoft Azure portal: New support request - Microsoft Azure

We value your feedback and suggestions to help us improve this sample. Please feel free to share your thoughts or start a conversation by opening an issue on the GitHub repository.

References

Updated Feb 04, 2026
Version 3.0
No CommentsBe the first to comment