TensorFlow Serving 2
Overview
TensorFlow Serving is Google's production-grade machine learning model server. It is a high-performance C++ data plane with native support for hot-swapping model versions, batching, and both gRPC and REST APIs.
This SecureAnyCloud image runs the official tensorflow/serving:2.19.1 Docker container under a thin systemd wrapper. A half_plus_two sample model is pre-staged, so you can validate inference end to end before you upload your own SavedModel. Models live at /data/models/<name>/<version>/ on the host and are mounted into the container at /models. Dropping a higher-numbered version directory hot-swaps the served model without restarting the server.
TensorFlow Serving has no built-in authentication and no web UI. In production, place it behind a reverse proxy, service mesh or API gateway. Administration is API-only, through REST on :8501, gRPC on :8500, or the bundled tensorflow_model_server CLI inside the container.
What you get:
- TensorFlow Serving 2.19.1 from the official Google Docker image
tensorflow/serving:2.19.1 - Sample model
half_plus_two(formula y = 0.5x + 2) staged at/data/models/half_plus_two/1/for instant inference validation - Docker CE 29.4.2 plus Docker Compose v2 plugin v5.1.3
- Container
tfservingmanaged bydocker composefrom/opt/tfserving/docker-compose.yml - Volume mount from host
/data/modelsto container/models - Ports
8500/tcp(gRPC) and8501/tcp(REST) - systemd units
tfserving.service(oneshot, ExecStart=docker compose up -d) andtfserving-firstboot.service(Before=tfserving.service) - Per-VM credentials log at
/stage/scripts/tfserving-credentials.logwithTFSERVING_VERSION,TFSERVING_REST_URL,TFSERVING_GRPC_URL,TFSERVING_SAMPLE_MODEL,TFSERVING_SAMPLE_PREDICT_URLandTFSERVING_API_KEY - 24/7 SecureAnyCloud support
Prerequisites
- An active Azure subscription, an SSH key, and a VNet and subnet
- Standard_B2s (4 GB RAM) as the minimum size for the sample model and small custom SavedModels. For production workloads, use Standard_D2s_v5 or larger, sized for your model's memory footprint and throughput.
- NSG inbound rules that allow
22/tcpfrom your management CIDR, and8501/tcp(REST) and8500/tcp(gRPC) only from clients that need direct access
TensorFlow Serving has no built-in authentication. Production deployments must put nginx, Envoy or a service mesh in front of these ports for auth and TLS. Do not expose 8500 or 8501 to the public internet without a reverse proxy.
Step 1-3: Deploy and connect over SSH
ssh azureuser@<vm-ip>Check service status and versions
sudo systemctl is-active docker.service tfserving.service tfserving-firstboot.service
sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
sudo docker exec tfserving tensorflow_model_server --versionAll three services report active. The tfserving container shows image tensorflow/serving:2.19.1 and an Up status. tensorflow_model_server --version reports the embedded TensorFlow build banner.
Read the per-VM credentials and URLs
sudo cat /stage/scripts/tfserving-credentials.logThe file defines the following values.
TFSERVING_REST_URL(defaulthttp://127.0.0.1:8501)TFSERVING_GRPC_URL(default127.0.0.1:8500)TFSERVING_SAMPLE_PREDICT_URL, the ready-made POST URL for the sample modelTFSERVING_API_KEY, a per-VM string for use with a downstream nginx auth layer. TF Serving itself does not enforce it.
The file is root:root 0600.
Check the sample model status
curl -sf http://127.0.0.1:8501/v1/models/half_plus_two | jqThe response shows model_version_status[0].state = "AVAILABLE" and version = "1". This shows that the SavedModel at /data/models/half_plus_two/1/ is loaded and ready to serve predictions. The status.error_code = "OK" field confirms that there were no load-time errors.
Run a prediction
The sample model implements y = 0.5x + 2, so inputs [1.0, 2.0, 5.0] return [2.5, 3.0, 4.5].
curl -sf -X POST -H 'Content-Type: application/json' \
-d '{"instances": [1.0, 2.0, 5.0]}' \
http://127.0.0.1:8501/v1/models/half_plus_two:predict | jqThe response is {"predictions": [2.5, 3.0, 4.5]}. This confirms the full inference path: the request reaches the container, the SavedModel runs the math, and the response comes back over REST. Once you drop a SavedModel into /data/models/, replace half_plus_two with your own model name.
Inspect the model metadata
curl -sf http://127.0.0.1:8501/v1/models/half_plus_two/metadata | jqThe signature_def block is the contract every client needs to call the model. It lists the named input tensors (with dtype and shape), the named output tensors and the signature method name, which is typically tensorflow/serving/predict or tensorflow/serving/classify. When you build a Python or Go client, query this endpoint first to discover the input and output tensor names without reading the SavedModel by hand.
Deploy your own model
TensorFlow Serving expects the on-disk layout <model_root>/<model_name>/<version>/saved_model.pb plus the variables/ and assets/ directories that tf.saved_model.save() produces. Copy your SavedModel into /data/models/.
sudo mkdir -p /data/models/my_model/1
sudo cp -r /path/to/saved_model/* /data/models/my_model/1/
sudo chown -R root:root /data/models/my_model
sudo systemctl restart tfserving.service sleep 5 curl -sf http://127.0.0.1:8501/v1/models/my_model | jqTensorFlow Serving detects new versions automatically. Add a higher-numbered version directory (for example /data/models/my_model/2/) to the same model root, and TF Serving hot-swaps to the new version on its next file-system scan, with no restart. The previous version is unloaded once the new one is fully ready, so in-flight requests are not dropped.
Serve multiple models
To serve more than one model at once, edit /opt/tfserving/models.config and list every model in the model_config_list block.
sudo tee /opt/tfserving/models.config <<'EOF' model_config_list { config { name: "half_plus_two" base_path: "/models/half_plus_two" model_platform: "tensorflow" } config { name: "my_model" base_path: "/models/my_model" model_platform: "tensorflow" } } EOF
sudo systemctl restart tfserving.service curl -sf http://127.0.0.1:8501/v1/models/my_model | jqMount the config file into the container by adding a --model_config_file=/models/models.config flag to the command: entry in /opt/tfserving/docker-compose.yml, replacing the single-model --model_name and --model_base_path flags. Then run docker compose up -d to apply the change.
Call the gRPC endpoint from Python
For lower-latency, higher-throughput access with binary protobuf instead of JSON, use the gRPC endpoint on :8500.
import grpc import tensorflow as tf from tensorflow_serving.apis import predict_pb2, prediction_service_pb2_grpc channel = grpc.insecure_channel('<vm-ip>:8500') stub = prediction_service_pb2_grpc.PredictionServiceStub(channel) request = predict_pb2.PredictRequest() request.model_spec.name = 'half_plus_two' request.model_spec.signature_name = 'serving_default' request.inputs['x'].CopyFrom( tf.make_tensor_proto([1.0, 2.0, 5.0], dtype=tf.float32) ) print(stub.Predict(request, 5.0))Install the client dependencies with pip install tensorflow tensorflow-serving-api grpcio. The gRPC API supports the same Predict, Classify, Regress, MultiInference and GetModelMetadata calls as REST. It uses proto-encoded tensors, which are roughly 30-60% smaller on the wire and avoid JSON parsing overhead on both ends.
Tune batching and resources
For throughput-sensitive workloads, enable server-side batching so that concurrent client requests are coalesced into a single GPU or CPU forward pass.
sudo tee /opt/tfserving/batching.config <<'EOF' max_batch_size { value: 32 } batch_timeout_micros { value: 5000 } num_batch_threads { value: 4 } max_enqueued_batches { value: 100 } EOFThen extend the command: array in /opt/tfserving/docker-compose.yml to include the following.
- --enable_batching=true - --batching_parameters_file=/models/batching.configMake the batching config available to the container by placing the file in /data/models, which maps to /models in the container. Apply the change with sudo systemctl restart tfserving.service. You can tune CPU resources per container with the cpus: and mem_limit: keys in docker-compose.yml.
For GPU inference, swap the image for tensorflow/serving:2.19.1-gpu and add the runtime: nvidia key. This requires the NVIDIA Container Toolkit on a GPU-equipped VM such as Standard_NC6s_v3.
Secure the endpoint with an nginx reverse proxy
TensorFlow Serving has no built-in auth, so a fronting nginx is the conventional way to add HTTP basic auth and TLS. You can also add mTLS, JWT or OAuth through a sidecar.
sudo apt-get update
sudo apt-get install -y nginx apache2-utils
sudo htpasswd -bc /etc/nginx/.tfserving.htpasswd tfclient "$(openssl rand -base64 24)"
sudo tee /etc/nginx/sites-available/tfserving <<'EOF' server { listen 80; server_name _; location / { auth_basic "TF Serving"; auth_basic_user_file /etc/nginx/.tfserving.htpasswd; proxy_pass http://127.0.0.1:8501; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } EOF
sudo ln -sf /etc/nginx/sites-available/tfserving /etc/nginx/sites-enabled/tfserving
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginxOnce nginx is in front, lock down the NSG. Make 8500/tcp and 8501/tcp reachable only from 127.0.0.1 (or your private VNet), and make only 80/tcp and 443/tcp publicly reachable.
Install a Let's Encrypt certificate
sudo apt-get install -y python3-certbot-nginx
sudo certbot --nginx -d apps.example.com -m you@example.com --agree-tos --non-interactive --redirectPoint your domain's A record at this VM first, then run certbot. The plugin edits the nginx server block in place to add listen 443 ssl;, the certificate paths and a 301 redirect from port 80. The systemd timer certbot.timer, which the Ubuntu package installs by default, handles renewal, so no further work is needed.
Back up your models
TensorFlow Serving is stateless, and its only persistent state is the SavedModel files on disk. A backup is a tarball of /data/models.
sudo tar -czf /var/backups/tfserving-models-$(date +%F).tgz -C /data models
sudo cp /opt/tfserving/docker-compose.yml /var/backups/docker-compose.yml-$(date +%F)For off-VM retention, periodically copy /var/backups to Azure Blob Storage with az storage blob upload-batch. You do not need to back up the container itself. The image is pulled from Docker Hub on demand, and the wrapper config lives in /opt/tfserving/, which is small enough to keep in version control.
Read logs and troubleshoot
sudo journalctl -u tfserving.service --no-pager -n 80
sudo journalctl -u tfserving-firstboot.service --no-pager -n 30
sudo docker logs tfserving --tail 80
sudo docker exec tfserving tensorflow_model_server --versiondocker logs tfserving -f follows the live server log. Every request hits the access log at INFO level, every load and unload is reported, and any malformed input tensor surfaces here as a structured error.
Common issues:
Could not find SavedModel: The on-disk layout is wrong. The required layout is/data/models/<model_name>/<version>/saved_model.pbplus thevariables/directory next to it. The<version>segment must be a positive integer.No versions of servable <name> found: The model directory is empty or contains only a non-numeric subdirectory. Rename the subdirectory to1/,2/and so on.HTTP 400 Could not parse request body: The JSON shape is wrong. The REST API expects{"instances": [...]}for predict and{"signature_name": "serving_default", "examples": [...]}for classify and regress.- Container restarts on boot: Confirm that
/var/lib/sac/data/tfserving-firstboot.doneexists. If it does not, runsudo systemctl start tfserving-firstboot.serviceand check again.
Security
- TensorFlow Serving has no built-in authentication. Production deployments must front it with nginx, Envoy, a service mesh or an API gateway that does its own auth. Do not expose
8500or8501directly to the public internet. - A per-VM
TFSERVING_API_KEYvalue is generated at first boot for downstream auth integration, for example nginxauth_requestagainst an upstream service that checks the header. TF Serving itself does not check it. - Open
22/tcpin the NSG inbound rules, plus only the proxy ports (80/tcpand443/tcpif you front the service with nginx). Never open8500/tcpor8501/tcpto the public internet. - The container runs as the unprivileged user inside the official
tensorflow/servingimage. By default, the host volume/data/modelsis mounted read-only at the container's/modelspath. - SavedModel files are loaded from
/data/models, and anything Docker can read on that host path becomes a servable. Do not put untrusted SavedModels there, because a malicious model can run arbitrary TensorFlow ops at load time. - Optional TLS at the edge via nginx and certbot (Step 14). Certificate renewals are automatic through
certbot.timer.
Support
SecureAnyCloud provides 24/7/365 expert technical support. Response is guaranteed within 24 hours, with a one-hour average for critical issues. Contact sales@secureanycloud.com.
Deploy on Azure
Launch TensorFlow Serving 2.19.1 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
TensorFlow Serving 2 is Google's flexible, high-performance serving system for machine learning models. This SecureAnyCloud image runs TF Serving 2 under Docker. It includes the half_plus_two sample SavedModel preloaded on a dedicated 20 GiB model volume, and an nginx basic-auth gateway that protects the REST API on port 80.
Connecting to your instance
OS variant Default login user Connect command
Ubuntu 24.04 ubuntu ssh -i your-key.pem ubuntu@<instance-public-ip>
First boot
On first boot, a one-shot systemd service (tfserving-firstboot.service) generates a fresh per-instance nginx password and writes it to /root/tensorflow-serving-credentials.txt. The TensorFlow Serving container and nginx then start automatically once first boot completes.
Retrieve your credentials with the following command.
sudo cat /root/tensorflow-serving-credentials.txtExample output:
# TensorFlow Serving 2 -- Per-VM Credentials
# Generated: Wed May 27 22:15:49 UTC 2026 TFSERVING_VERSION=2.19.1 NGINX_USER=sac password=5802da51dc7fd2e3616d5a8072c2134feeab80f0eb3137f0 TFSERVING_REST_URL=http://32.198.94.22:8501 TFSERVING_GRPC_URL=32.198.94.22:8500 TFSERVING_REST_GATED_URL=http://32.198.94.22/v1 TFSERVING_SAMPLE_MODEL=half_plus_twoVerifying the service
Check that the systemd unit and Docker container are running.
systemctl status tfserving.serviceExample output:
* tfserving.service - TensorFlow Serving 2 Model Server (sac) Loaded: loaded (/etc/systemd/system/tfserving.service; enabled; preset: enabled) Active: active (exited) since Wed 2026-05-27 22:15:52 UTC; 18s ago Process: 33573 ExecStart=/usr/bin/docker compose -f /opt/tfserving/docker-compose.yml up -d (code=exited, status=0/SUCCESS) Main PID: 33573 (code=exited, status=0/SUCCESS) CPU: 99ms May 27 22:15:52 ip-172-31-85-204 systemd[1]: Starting tfserving.service ... May 27 22:15:52 ip-172-31-85-204 docker[33587]: Container tfserving Started May 27 22:15:52 ip-172-31-85-204 systemd[1]: Finished tfserving.service ...Check the model server version.
docker exec tfserving tensorflow_model_server --versionOutput:
TensorFlow ModelServer: 2.19.1-rc0 TensorFlow Library: 2.19.1Querying the model server
Health check (basic-auth gated, port 80)
PASS=$(sudo awk -F= '/^password=/{print $2}' /root/tensorflow-serving-credentials.txt)
curl -s -u "sac:${PASS}" http://127.0.0.1/v1/models/half_plus_two | python3 -m json.toolOutput:
{ "model_version_status": [ { "version": "1", "state": "AVAILABLE", "status": { "error_code": "OK", "error_message": "" } } ] }REST predict (half_plus_two: y = 0.5x + 2)
PASS=$(sudo awk -F= '/^password=/{print $2}' /root/tensorflow-serving-credentials.txt)
curl -s -u "sac:${PASS}" \
-X POST -H 'Content-Type: application/json' \
-d '{"instances": [1.0, 2.0, 5.0]}' \
http://127.0.0.1/v1/models/half_plus_two:predict | python3 -m json.toolOutput:
{ "predictions": [ 2.5, 3.0, 4.5 ] }Raw REST endpoint (port 8501, unauthenticated)
Port 8501 is also published directly on the host for clients that need to bypass nginx.
curl -s http://127.0.0.1:8501/v1/models/half_plus_two | python3 -m json.toolgRPC endpoint (port 8500, unauthenticated)
The gRPC endpoint is available on port 8500. TF Serving has no built-in gRPC authentication, so front it with an API gateway or service mesh for public-facing workloads.
Verify that the port is open.
nc -zv 127.0.0.1 8500Output:
Connection to 127.0.0.1 8500 port [tcp/*] succeeded!Serving your own SavedModel
TF Serving expects models in the layout <model_base>/<model_name>/<version>/saved_model.pb.
- Copy your SavedModel to the data volume.
sudo mkdir -p /var/lib/tfserving/models/my_model/1/
sudo cp -r /path/to/saved_model.pb /var/lib/tfserving/models/my_model/1/
sudo chown -R root:root /var/lib/tfserving/models/my_model/- Update the compose file to load your model name.
sudo sed -i 's/MODEL_NAME: half_plus_two/MODEL_NAME: my_model/' /opt/tfserving/docker-compose.yml- Restart the stack.
sudo systemctl restart tfserving.service- Query your model.
PASS=$(sudo awk -F= '/^password=/{print $2}' /root/tensorflow-serving-credentials.txt)
curl -s -u "sac:${PASS}" http://127.0.0.1/v1/models/my_model | python3 -m json.toolModel storage volume
Model files live on a dedicated 20 GiB gp3 EBS volume mounted at /var/lib/tfserving. You can resize this volume independently of the OS disk.
df -h /var/lib/tfservingOutput:
Filesystem Size Used Avail Use% Mounted on /dev/nvme1n1 20G 64K 19G 1% /var/lib/tfservingTo expand the volume, resize it in the EC2 console, then run sudo resize2fs /dev/nvme1n1.
Managing the service
Start, stop or restart TensorFlow Serving with the following commands.
sudo systemctl start tfserving.service
sudo systemctl stop tfserving.service
sudo systemctl restart tfserving.serviceView the container logs.
docker logs tfserving --tail 50Enabling TLS
Terminate TLS at nginx with a certificate from Let's Encrypt or your own CA.
- Install certbot with
sudo apt install certbot python3-certbot-nginx. - Obtain a certificate with
sudo certbot --nginx -d your-domain.example.com. - Certbot updates the nginx site automatically.
Security recommendations
- Ports 8500 and 8501 are published unauthenticated on the host. Restrict the security group rules for those ports to trusted private CIDRs, or remove them and route all traffic through nginx on port 80.
- The nginx basic-auth password is stored in
/root/tensorflow-serving-credentials.txt(root-only). For production deployments, replace basic auth with a more robust mechanism such as mTLS, an OAuth2 proxy or an API gateway.
Stopping and restarting on boot
TensorFlow Serving and nginx are enabled on boot through systemd. To prevent the model server from starting on the next boot, run the following command.
sudo systemctl disable tfserving.serviceSupport
This image is supported by SecureAnyCloud. For technical assistance, contact sales@secureanycloud.com.
Deploy on AWS
Launch TensorFlow Serving 2 with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com