This is a serverless application that uses Z-Image-Turbo (via Diffusers) to run a Text-to-Image task on Modal (recommended) or RunPod.
Serverless means that you are only charged for the time you use the application, and you don't need to pay for the idle time, which is very suitable for this kind of application that is not used frequently but needs to respond quickly.
All traffic is encrypted: prompts and result images are Fernet-encrypted between client and server.
Theoretically, this application can be called by any other application. Here we provide a simple Python client for each platform. See Usage below for more details.
Input Prompt: (seed: 42, 1088x1920)
A young woman sitting inside a cafe by a large window on a rainy evening. Long black hair, natural bare face, subtle skin detail, light smile, looking directly at the camera. Soft beige cropped knit top, khaki shorts. Both hands holding a single coffee cup on a bare wooden table. Raindrops on the glass, pedestrians with umbrellas passing outside, blurred street. Photograph, warm interior light, shallow depth of field, face and upper body in sharp focus, neutral warm tones, shot on 35mm film, photorealistic.
Result: (9 steps, around 12 seconds on Modal A100, measured end-to-end: from sending the encrypted request to saving the decrypted image, warm container)
Input Prompt: (seed: 52, 1280x720)
A fluffy white cat sitting on a sunlit windowsill among potted green plants, golden hour sunlight streaming through the window, dust motes glowing in the light, cozy room with wooden bookshelves in soft-focus background, shallow depth of field, photorealistic, ultra-detailed fur, warm tones
Result: (9 steps, around 5.5 seconds on Modal A100, measured end-to-end)
- Python >= 3.10, < 3.13
- uv
- Modal account (for Modal deployment)
- Docker + DockerHub + RunPod accounts (for RunPod deployment)
- Local GPU is necessary for testing but not necessary for deployment.
Input format (see test_input.json; only prompt is required, width/height must be multiples of 16, guidance_scale is fixed at 0.0):
{"input": {"prompt": "A fluffy white cat sitting on a sunlit windowsill", "width": 1280, "height": 720, "steps": 9, "seed": 52, "n_samples": 1}}Models are baked into the image during build — no Docker required, one-command deploy, GPU snapshot cold starts.
- Set up and deploy:
# Install the deploy/client toolchain (lightweight — the GPU stack lives in the Modal image)
uv sync
# Generate an encryption key and save it to .env (required, shared by server and client)
uv run python crypto_util.py
echo "ENCRYPTION_KEY=<your-key>" > .env
# Set Modal token
uv run modal token set --token-id <id> --token-secret <secret>
# Create secrets (HF token for model download during build, encryption key for the endpoint)
uv run modal secret create huggingface-secret HF_TOKEN=<hf_token>
uv run modal secret create encryption-secret ENCRYPTION_KEY=<your-key>
# Deploy (prints the endpoint URL)
uv run modal deploy app.pyThe first request after a deploy takes a few minutes (model loading + GPU snapshot creation). After that, cold starts take 20-40 seconds and warm requests a few seconds.
- Call the endpoint:
uv run python client_modal.py --url https://<your-endpoint>.modal.runExpand for RunPod deployment (Docker build + DockerHub push required)
Requires a local GPU. Verify the worker before building and pushing the image.
# Install dependencies incl. the local GPU stack (torch, diffusers, ...)
uv sync --extra local
# Generate an encryption key and save it to .env (required, shared by server and client)
uv run python crypto_util.py
echo "ENCRYPTION_KEY=<your-key>" > .env
# Download models
uv run hf download Tongyi-MAI/Z-Image-Turbo --local-dir models/Z-Image-Turbo
# Edit (or not) config.yaml to customize your inference, e.g., change model repo, quantization, default size, etc.
# Run inference test
uv run python inference_util.py
# Run server.py local test (consumes test_input.json)
ALLOW_PLAINTEXT=1 uv run python server.py-
First, make sure you have installed Docker and have accounts on both DockerHub and RunPod.
-
Then, decide a name for your Docker image, e.g., "your_username/zit:v1" and set it as
runpod.docker_imageinconfig.yaml. -
Run the following commands to build and push your Docker image to DockerHub.
bash scripts/build.sh- Finally, deploy your application on RunPod to create Template and Endpoint. Set the Model field to the same repo as
model.repo_idinconfig.yaml(models are loaded from RunPod's HuggingFace cache, not baked into the image), and setENCRYPTION_KEYin the endpoint environment variables.
# Set RUNPOD_API_KEY and RUNPOD_ENDPOINT_ID in .env
uv run python client_runpod.pyThanks to Tongyi-MAI (Z-Image-Turbo), Hugging Face Diffusers, Modal, and RunPod.

