REBRANDING NOTICE: EzDeploy is now Roptal. Platform launching on roptal.com. Docs: docs.roptal.com.REBRANDING NOTICE: EzDeploy is now Roptal. Platform launching on roptal.com. Docs: docs.roptal.com.REBRANDING NOTICE: EzDeploy is now Roptal. Platform launching on roptal.com. Docs: docs.roptal.com.REBRANDING NOTICE: EzDeploy is now Roptal. Platform launching on roptal.com. Docs: docs.roptal.com.
Oryvo
← All articles

Deploy a Hugging Face Model to AWS SageMaker: Docker, IAM, and Endpoint Setup

A practical SageMaker deployment guide covering FastAPI handlers, production Dockerfiles, IAM roles, ECR, health checks, and endpoint sizing.

Deploy a Hugging Face Model to AWS SageMaker: Docker, IAM, and Endpoint Setup

Hugging Face has an AWS integration, but production deployment still comes down to three things: a container image, an IAM role, and a SageMaker endpoint configuration. This guide covers the parts that usually cause failed deployments.

The Deployment Shape

For a custom Hugging Face model, the request path is:

Client -> SageMaker endpoint -> Docker container -> FastAPI handler -> model

SageMaker does not need to know whether you use transformers, PyTorch, or a custom tokenizer. It starts a container and routes HTTP requests to it. Your container needs to expose a predictable handler and return JSON.

Start With a Small FastAPI Handler

from fastapi import FastAPI
from pydantic import BaseModel
from transformers import pipeline

app = FastAPI()
classifier = pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english")

class PredictionRequest(BaseModel):
    text: str

@app.get("/health")
def health():
    return {"status": "ok"}

@app.post("/predict")
def predict(request: PredictionRequest):
    return classifier(request.text)[0]

Keep the health endpoint separate from inference. An endpoint can have a running web process but a failed model load. Your health check should only return 200 after the model is ready.

Dockerfile Requirements

FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
RUN useradd --create-home --uid 1000 appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Use the CUDA runtime image rather than the development image. It avoids shipping compilers and headers that are unnecessary at inference time.

IAM Role: Minimum Permissions

The SageMaker execution role needs access to the image in ECR and any model artifacts stored in S3.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::your-model-bucket/*"
    }
  ]
}

Avoid attaching AdministratorAccess just to get the first deployment working. It makes later security review much harder.

Build and Push to ECR

aws ecr create-repository --repository-name sentiment-api
aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com

docker build -t sentiment-api .
docker tag sentiment-api:latest ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/sentiment-api:git-sha
docker push ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/sentiment-api:git-sha

Tag images with a Git SHA or release version. Do not deploy latest; it makes rollback ambiguous.

Endpoint Sizing

For a small BERT-style classifier, ml.g4dn.xlarge is a reasonable starting point:

  • NVIDIA T4 GPU with 16GB VRAM
  • 4 vCPU
  • 16GB system memory
  • Around $0.35/hour on-demand in us-east-1

For low traffic, CPU inference may be cheaper. Run a load test first. A model that takes 30ms on GPU but receives 100 requests per day does not need a permanent GPU endpoint.

Common Deployment Failures

Endpoint stuck in Creating

Usually an ECR pull failure or container crash. Check CloudWatch logs for ModuleNotFoundError, missing system libraries, or an incorrect entrypoint.

Health check fails but the container starts

The model probably loads after the health check starts. Add a startup grace period or only expose the health endpoint after model initialization completes.

CUDA out of memory

Use FP16 for compatible models, reduce batch size, or move to a GPU with more memory. Do not solve it by restarting the endpoint repeatedly.

A Better Operating Model

The container build is only one part of production. You also need a release strategy, logs, health monitoring, rollback, and cost controls. Roptal connects the repository, generates reviewable deployment files, and deploys the same image to your AWS account without taking ownership of your infrastructure.