Ever wanted to create your own AI-powered image generator? In this tutorial, I'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!
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
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..." }
You'll need a few things ready:
| What You Need | Why |
|---|---|
| Azure account | Create a free account if you don't have one |
| GPU access | GPUs require special quota approval. Request access here - it usually takes a day or two |
| Azure CLI | Download 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!
Here's the simple flow:
Your App β Azure Function on Container Apps β Stable Diffusion (on GPU) β Image!
π± β‘ π¨ πΌοΈ
The magic happens inside an Azure Function running on Azure Container Apps with GPU access. When a request comes in:
- The function receives your text prompt
- Stable Diffusion (running on a Tesla T4 GPU) generates the image
- You get back a base64-encoded PNG image
First, grab the sample code from GitHub:
git clone https://github.com/Azure-Samples/function-on-aca-gpu.git
cd function-on-aca-gpuHere'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!
Let's look at the key parts. Don't worry - it's simpler than it looks!
The image generation function (function_app.py):
@app.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.
You have two options: command line scripts (faster, recommended) or the Azure Portal (great for learning).
Use our one-click deployment script - it does everything for you!
On Windows (PowerShell):
cd function-on-aca-gpu
.\deploy.ps1On 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:
- Create a resource group for all our stuff
- Set up a container registry to store our Docker image
- Build and upload the Docker image (no Docker Desktop needed!)
- Create a Container Apps environment with GPU support
- Deploy the function app
- 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
============================================
If you prefer clicking through a UI, follow these steps:
Part 1: Create a Container Registry
-
Go to the Azure Portal and search for Container Registries
-
Click + Create
-
Fill in the details:
Setting Value Subscription Select your subscription Resource group Create new β gpu-functions-rgRegistry name gpufunctionsacr(must be globally unique)Location Sweden CentralSKU Standard -
Click Review + create β Create
-
Once created, go to the registry β Settings β Access keys
-
Enable Admin user and note the Login server, Username, and Password
Part 2: Build and Push the Docker Image
Since we can't build Docker images directly in the portal, use Azure Cloud Shell:
- Click the Cloud Shell icon (>_) in the top navigation bar
- Choose Bash
- 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
-
Search for Container Apps Environments and click + Create
-
Fill in the Basics tab:
Setting Value Subscription Select your subscription Resource group gpu-functions-rgEnvironment name gpu-functions-envRegion Sweden CentralEnvironment type Workload profiles -
Click Workload profiles tab β + Add workload profile
-
Configure the GPU profile:
Setting Value Workload profile name gpu-profileWorkload profile size Consumption - GPU NC8as-T4 -
Click Add β Review + create β Create
Part 4: Create the Container App with Azure Functions
-
Search for Container Apps and click + Create β Container App
-
Fill in the Basics tab:
Setting Value Subscription Select your subscription Resource group gpu-functions-rgContainer app name gpu-image-gen-funcOptimize for Azure Functions β Check this box! (This is important - it enables Azure Functions support) Region Sweden CentralContainer Apps Environment Select gpu-functions-env -
Click Next: Container > and fill in:
Setting Value Use quickstart image β Uncheck Name gpu-image-gen-containerImage source Azure Container RegistryRegistry gpufunctionsacrImage gpu-image-genImage tag latestWorkload profile gpu-profileGPU β Check this box -
Add environment variables at the bottom of the Container tab:
Name Value MODEL_IDrunwayml/stable-diffusion-v1-5FUNCTIONS_WORKER_RUNTIMEpython -
Click Next: Ingress > and configure:
Setting Value Ingress β Enabled Ingress traffic Accepting traffic from anywhereTarget port 80 -
Click Review + create β Create
π Done! Once deployed, find your function URL under Overview β Application Url
Now that your function is deployed, let's make sure everything works!
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.
# 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"β±οΈ First request is slow (1-2 minutes) because it downloads the AI model (~5GB). After that, images generate in just a few seconds!
Play around with different prompts! Here are some ideas:
| Prompt | What 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"
Here's everything you can send to the /api/generate endpoint:
| Parameter | Type | Default | What It Does |
|---|---|---|---|
prompt |
string | required | Describe what you want to see |
negative_prompt |
string | "" |
What to avoid (e.g., "blurry, ugly") |
num_steps |
int | 25 |
More steps = better quality but slower |
guidance_scale |
float | 7.5 |
Higher = follows prompt more strictly |
width |
int | 512 |
Image width (keep at 512 for best results) |
height |
int | 512 |
Image 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
}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:
Tell Azure to always keep one container running:
az functionapp config set \
--name gpu-image-gen-func \
--resource-group gpu-functions-rg \
--min-replicas 1This makes the container start faster:
az acr artifact-streaming update \
--name gpufunctionsacr \
--repository gpu-image-gen \
--enableGood news - this can be very affordable! Here's where to find pricing for each resource:
| Resource | Pricing Info |
|---|---|
| Azure Container Apps (GPU) | Container Apps Pricing - GPU workload profiles section |
| Azure Container Registry | ACR Pricing |
| Azure Functions | Functions Pricing - Container Apps hosting |
Key cost-saving tips:
- π‘ Set
min-replicasto 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
Don't want to keep paying? Delete everything with one command:
az group delete --name gpu-functions-rg --yesThis removes all the resources we created. You can always redeploy later!
"Model failed to load" error?
- Some models require a Hugging Face account. We use
runwayml/stable-diffusion-v1-5which works without authentication.
Images look weird?
- Try adding
"blurry, distorted, low quality"tonegative_prompt - Increase
num_stepsto 30 or 40
Function times out?
- First request can take 2+ minutes. Be patient!
- Check GPU is available with the
/api/healthendpoint
GPU not detected?
- Make sure GPU quota was approved
- Verify
gpu-profileworkload profile was created
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!
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? I'd love to hear about it!