TensorFlow Serving: Model Deployment
Serve models with TensorFlow Serving — Applied AI engineering.
Focus: serve models with tensorflow serving
Your model crushes validation metrics in the notebook, but the moment your team tries to call it from a real app, everything falls apart. The Flask prototype you hacked together dies under load, the versioning is a mess, and rolling back to a previous model means redeploying the entire service. This is the classic gap between training and production — and it's exactly the pain TensorFlow Serving was built to eliminate. By the end of this lesson, you'll be able to serve a TensorFlow model as a production-grade, versioned REST API with zero custom serving code.
The problem this lesson solves
In production, models rarely sit inside Jupyter notebooks. A web app, a mobile backend, or a batch job needs to send a request and get a prediction back quickly and reliably. The naive approach — loading the model in Python and wrapping it in a Flask endpoint — hits walls fast:
- Performance: Flask is not designed for high-throughput ML inference. Every request triggers Python's Global Interpreter Lock (GIL), and batch predictions become a bottleneck.
- Versioning: When you retrain, you overwrite your model file. If the new model underperforms, you can't instantly roll back — you have to find the old weights and redeploy.
- Resource management: Each new deployment is a new server process. Memory balloons, and you end up running multiple servers just to keep models alive.
- Scaling: A homegrown server usually can't handle autoscaling, load balancing, or graceful shutdowns on its own.
TensorFlow Serving solves this by providing a dedicated, high-performance serving system for machine learning models. It handles model loading, versioning, and inference with near-zero boilerplate — you just point it at a directory of exported models and it serves models with TensorFlow Serving automatically.
Core concept / mental model
Think of TensorFlow Serving as a smart restaurant kitchen. The model is the recipe, and the trained weights are the ingredients. Instead of cooking each dish (prediction) from scratch in a single pot (your app), you have a chef (the serving server) that preps everything in advance, keeps multiple recipes ready, and can switch between them instantly when a new version comes in.
Key terms to know:
- SavedModel: The standard TensorFlow export format. It's a directory containing the model's graph, variables, and signature definitions — everything needed to run inference.
- Model Server: The
tensorflow_model_serverbinary that loads SavedModels and exposes a gRPC and REST API. - Model Version: Each exported SavedModel can be stored in a numbered subdirectory (e.g.,
1/,2/). The server loads the latest by default, but you can pin a specific version in the request. - Signature: A named input/output contract. The default serving signature,
serving_default, defines what tensors the model expects and returns.
Visualize the architecture:
Your App → HTTP/REST → TensorFlow Serving (port 8501) → SavedModel on disk
The server is a standalone process, separate from your Python application. Your app makes HTTP requests to get predictions — no need to import TensorFlow in your web backend.
How it works step by step
Serving a model with TensorFlow Serving follows a simple, repeatable pipeline. Here's the logical flow from training to serving:
-
Train and export: Train your model in TensorFlow, then save it with
tf.saved_model.save(). This creates a directory with asaved_model.pbfile and variables. -
Organize the model repository: Create a root directory (e.g.,
/models) and put each exported model in its own subdirectory named with a version number — e.g.,/models/my_model/1/,/models/my_model/2/. The server scans this structure to discover models. -
Run the TensorFlow Serving server: Launch
tensorflow_model_serverwith the--model_base_pathflag pointing to the root directory. The server loads the latest version and listens on port 8501 for REST and 8500 for gRPC. -
Send inference requests: Your client sends a JSON payload with the input tensor and signature name. The server returns predictions as JSON.
-
Monitor and manage versions: The server watches the model repository for changes. When a new version directory appears, it loads it and makes it the default — no restart needed. You can also set
--model_config_filefor more control.
Cause and effect: because the server owns model lifecycle, your application code stays thin. You just change the model version in a request or let the server auto-roll to the latest.
Hands-on walkthrough
Let's serve a real model. We'll train a tiny Keras model, export it, and serve it with TensorFlow Serving. Make sure TensorFlow Serving is installed (either via Docker or the binary). We'll use Docker for simplicity.
Step 1: Train and export a model
Create a Python script train_export.py:
import tensorflow as tf
# Build a simple classifier
model = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation='relu', input_shape=(4,)),
tf.keras.layers.Dense(3, activation='softmax')
])
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
# Dummy data
import numpy as np
x_train = np.random.rand(1000, 4).astype('float32')
y_train = tf.keras.utils.to_categorical(np.random.randint(0, 3, 1000), num_classes=3)
model.fit(x_train, y_train, epochs=2, verbose=0)
# Export as SavedModel
tf.saved_model.save(model, "models/my_model/1/")
print("Model exported to models/my_model/1/")
Run it:
python train_export.py
This creates the directory models/my_model/1/ with a saved_model.pb file and variables.
Step 2: Start TensorFlow Serving with Docker
docker run -p 8501:8501 \
--mount type=bind,source=$(pwd)/models,target=/models \
-e MODEL_NAME=my_model \
-t tensorflow/serving
This maps port 8501 for REST requests. The server will find the model at /models/my_model/1/ and expose it as my_model.
Expected output: you'll see logs like Exporting HTTP/REST API at:localhost:8501 ... and Reading SavedModel from: /models/my_model/1.
Step 3: Query the served model
Now send a prediction request. The REST endpoint is POST /v1/models/{model_name}:predict.
import requests
import numpy as np
# Prepare input — exactly one sample with 4 features
payload = {
"instances": [[2.0, 1.0, 0.5, -1.0]]
}
response = requests.post(
"http://localhost:8501/v1/models/my_model:predict",
json=payload
)
print(response.json())
Example output:
{"predictions": [[0.31, 0.45, 0.24]]}
That's it — your model is now a live API! Test with a different input or use gRPC for lower latency if you need it.
Compare options / when to choose what
TensorFlow Serving isn't the only game in town. Let's compare it with other common serving approaches.
| Approach | Pros | Cons | Best for |
|---|---|---|---|
| TensorFlow Serving | Handles versioning, batching, and scaling; supports gRPC & REST; low latency | Requires model export; heavier infrastructure | Production TF models needing high throughput |
| Flask/FastAPI + tf.keras | Simple, full control, easy to integrate with your app | No built-in versioning; performance bottleneck; manual scaling | Prototypes, internal tools, low traffic |
| ONNX Runtime Serving | Model-agnostic, lightweight | Requires ONNX export; less TF-specific features | Cross-framework deployments |
| TensorFlow Lite / TF.js | Runs on edge/browsers | Limited model support; lower accuracy if quantized | Mobile, IoT, browser inference |
Pro tip: If your model is purely a Keras or TF model and you need versioned, high-performance serving, TensorFlow Serving is the clear winner. For a microservice that also needs custom logic, FastAPI with
tf.saved_model.load()might be simpler — but you'll have to build versioning and scaling yourself.
Troubleshooting & edge cases
Even with a dedicated server, things can go wrong. Here are the most common issues and how to fix them.
Model not found in repository
- Error:
Could not find base path /models/my_model. Check that the--model_base_pathpoints to the parent directory, not the versioned subdirectory. The correct structure is/models/my_model/1/. Also verify the directory permissions — the server must read the files.
REST request returns 404
- You might be using the wrong URL. For the default signature, the endpoint is
/v1/models/{model_name}:predict. If you specified a model version, include it as/v1/models/{model_name}/{version}:predict.
Input shape mismatch
- Error like
input size does not match. Ensure the JSONinstancesfield is a 2D array (even for a single sample). If your model expects shape(None, 4), send[[1,2,3,4]]not[1,2,3,4].
Server takes too long to load model
- Large models can take a while. Check the logs. If you have memory constraints, set
--max_num_load_retriesor use--enable_batchingto optimize throughput.
Multiple versions cause confusion
- By default, the server loads the highest-numbered version. If you want a specific version, use
--model_version_policyin the config file or request with version in the URL.
Pro tip: Always check the server logs — they tell you exactly which model version is loaded and any errors during loading.
docker logs <container>is your first debugging tool.
What you learned & what's next
You now know how to serve models with TensorFlow Serving — from exporting a SavedModel to launching a production server and querying it via REST. You understand the mental model of a model repository, versioning, and the clear separation between your application code and the inference server.
Key takeaways to carry forward:
- TensorFlow Serving separates inference from your application code, improving performance and reliability.
- Models are exported as SavedModel directories with versioned subdirectories.
- REST endpoint
/v1/models/{model_name}:predictgives you instant predictions. - Versioning is automatic — the server picks the latest version, and you can pin versions in requests.
- Docker is the fastest way to get started, but the native binary works too.
Next up in the track: Now that your model is served, you'll learn how to monitor and scale it — or integrate it with a larger data pipeline. Look for the next lesson on model observability and performance tuning in production systems.
Practice recap
Train a small Keras model on any dataset, export it as a SavedModel, and serve it with TensorFlow Serving using Docker. Send at least three different prediction requests and confirm you get valid outputs. Then, add a second version of the model (e.g., with a different architecture) and verify the server auto-switches to the new version without a restart.
Common mistakes
- Pointing the model server at the versioned subdirectory instead of the parent model base path — the server needs the parent directory that contains the version folders.
- Sending input as
instancesbut forgetting to wrap a single sample in an extra list, causing shape mismatches. - Forgetting to set
-e MODEL_NAME=my_modelin Docker, which makes the endpoint name default and causes 404s on the intended URL. - Assuming the server auto-reloads new versions without any polling — it does, but only if you keep the version directories under the same base path and let it manage versions.
Variations
- Use FastAPI to build a thin server that loads the SavedModel with
tf.saved_model.load()— gives custom endpoints but lacks built-in versioning and batching. - Deploy TensorFlow Serving inside a Kubernetes cluster via Helm charts to get autoscaling and rolling updates for your model services.
- Leverage gRPC instead of REST for production — it's faster and uses
predict.futuresfor streaming predictions.
Real-world use cases
- A fraud-detection service that handles thousands of HTTP requests per second, using TensorFlow Serving's batching to keep latency under 10 ms.
- A recommendation engine that frequently retrains models and needs to roll out new versions instantly without downtime.
- A mobile app backend that serves a lightweight TensorFlow model to classify images, using the versioned API to A/B test model upgrades.
Key takeaways
- TensorFlow Serving is a dedicated, high-performance server that decouples model inference from your application code.
- Models are exported as SavedModel directories, and versioned subdirectories enable automatic and manual version control.
- The REST API follows
/v1/models/{model_name}:predictand accepts JSONinstancespayloads. - Docker makes deployment trivial: mount a model repo and set
MODEL_NAMEto expose the model. - Always verify your model's input shape and the server logs when debugging — most issues come from path or shape mistakes.
- Choose TensorFlow Serving over homegrown Flask servers when you need production-grade scaling, versioning, and batch inference.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.