Python applications often need more than the packages from PyPI. An OCR service may need the Tesseract executable and its language data. A media app depends on FFmpeg. A document processor reaches for Poppler or LibreOffice. A local AI pipeline may need model weights and native numerical libraries.
Those dependencies are part of the application, even when they are not listed in requirements.txt. A container image defines the Python runtime, operating-system packages, model assets, and startup command together. Vercel builds that image, runs it as a Function, and deploys it alongside the rest of your application.
This guide covers building a document extractor with Python and Next.js, an app that pulls text out of PDFs and images. It also covers when to use Docker and how to deploy Dockerized apps to Vercel.
Deploy the template now, or keep reading to build it yourself.
FastAPI + Next.js OCR template
Upload a PDF or image and extract its text in seconds using OCR.
Copy link to headingQuick start with an AI coding agent
I want to build a document text extraction app using the FastAPI + Next.js Docker OCR template. Read the setup instructions at https://agent-resources.dev/ocr-fastapi-nextjs-docker-template.md and follow them. They cover deploying the template with Vercel Services, running it locally with the Vercel CLI and Docker, working with pypdf and Tesseract OCR, and understanding how PDF and image uploads are processed.
This guide covers the core implementation. The AI assistant prompt above covers all the details your coding agent needs, and you can see the full implementation in the template repository.
Copy link to headingVercel Plugin
The Vercel Plugin turns your AI coding agent (e.g., OpenAI Codex, Claude Code, or Cursor) into a Vercel expert. It adds skills, slash commands, and current knowledge of the tools this template uses. The plugin is optional; it isn't required to use the template or to follow this guide.
Copy link to headingWhen to use Docker
Vercel's native Python runtime supports deploying Python applications, including popular frameworks like FastAPI, Flask, and Django. It installs packages from pyproject.toml, requirements.txt, or a Pipfile, and can deploy with little or no platform configuration. Use that path when the application and its runtime dependencies can be installed entirely through the supported Python environment.
Use a container image when the runtime also needs one or more of the following:
- An operating-system executable such as Tesseract, FFmpeg, or Chromium.
- Shared libraries or language data that a Python wheel does not include.
- A framework or server that Vercel does not detect natively.
- A small CPU model whose runtime and versioned weights need to ship together.
- The same OCI image in local development and production.
Docker does not turn the deployment into a general-purpose virtual machine. Vercel builds the image, stores it in Vercel Container Registry, and runs it as an autoscaling Vercel Function on Fluid compute. The service must serve HTTP, remain stateless, scale down when idle, and adhere to Function limits on size, memory, and duration. To learn more, see container execution model.
Copy link to headingWhy this project needs a container
The pytesseract Python dependency is a wrapper around the Tesseract OCR engine. Installing it via pip does not install the tesseract executable. The executable must be installed separately and available on PATH, which is why the pytesseract installation instructions list tesseract-ocr as a Debian or Ubuntu prerequisite.
The project, therefore, has two dependency layers:
requirements.txtinstalls FastAPI, Uvicorn, Pillow,pypdf,python-multipart, andpytesseract.- The Dockerfile installs the
tesseract-ocroperating-system package.
pypdf is not OCR software, so it can extract existing PDF text but cannot recognize words from a scanned page image. Scanned PDF support requires an additional rendering step before Tesseract can process each page.
Copy link to headingHow to use Docker
Vercel looks for Dockerfile.vercel (or Containerfile.vercel) in the root directory. In this repository, the API service points to apps/api/Dockerfile.vercel:
Here’s a brief breakdown of the above Dockerfile:
python:3.12-slimsupplies Python and Debian's package manager without the larger general-purpose image. TheENVvalues prevent bytecode files, make logs appear immediately, and provide a local port default.- The first
RUNinstruction updates the package index and installs Tesseract in the same layer, skips recommended packages that the service does not need, and removes the temporary package index afterward. These choices follow Docker's guidance for Debian-based images. - The dependency manifest is copied before the application source so Docker can reuse the package layer when only Python code changes. The final command binds Uvicorn to
0.0.0.0and uses$PORT; Vercel routes to port80by default unless thePORTenvironment variable is configured.
Copy link to headingWhat Vercel Services does
Docker defines the API's execution environment. Vercel Services controls how the API and frontend are built, deployed to a single project, and routed.
The repository's vercel.json declares two services:
The web service is built with Vercel's Next.js integration. The api service uses apps/api as its build context and Dockerfile.vercel as its container entrypoint. Each service has its own dependencies and build, but they belong to one deployment.
The rewrites map public paths to services:
/api/*is handled by the container service.- Every other path is handled by Next.js.
FastAPI includes /api in its route definitions because the original request path reaches the service. The browser calls /api/extract on the same origin as the page, so the project does not need a separate API hostname or routine CORS configuration.
Container services run as Vercel Functions, not as permanent Docker hosts. Instances are stateless, may be reused while warm, scale with traffic, and can disappear when idle. Do not use local memory or disk as the system of record.
Copy link to headingHow the application works
The browser sends a multipart/form-data request to /api/extract. Vercel routes that path to FastAPI without exposing a second public origin. FastAPI reads the file into memory, enforces the application limit, and chooses an extractor from its content type or filename extension.
PDF and image extraction take different paths:
| Input | Processing path | What it can read |
|---|---|---|
pypdf.PdfReader | Text already embedded in PDF pages | |
| Image | Pillow → pytesseract → Tesseract | Pixels containing recognizable text |
pypdf does not turn scanned pages into text. A scanned PDF can, therefore, return an empty result even when the pages visibly contain words. Supporting scans requires rendering each page to an image before running OCR.
These files define the architecture:
apps/api/Dockerfile.verceldefines the Python container image.apps/api/app/main.pyexposes the FastAPI endpoints.apps/api/app/extractor.pyselects PDF extraction or image OCR.apps/web/components/upload-workbench.tsxsends documents to the API.vercel.jsondeclares and routes the two services.
Copy link to headingRun locally and deploy
Run the complete multi-service project with:
This starts the services from the repository root and applies the routing in vercel.json.
To deploy, run vercel deploy from the project root or follow these steps:
- Import the repository root as a Vercel project.
- Confirm that the project's framework setting is set to Services. If not, update it.
- Deploy the repository.
- Upload a small PDF and a small image, then confirm both extraction paths.
Copy link to headingNext steps
You can make this application even more robust by adding storage, AI processing, asynchronous jobs, and production controls:
- Add Vercel Blob: Upload larger documents directly from the browser, retain source files for retries, and store generated assets.
- Add AI processing: Use the AI SDK after extraction for summaries, structured fields, classification, tagging, or document chat.
- Persist jobs and results: Store processing status, metadata, extracted text, and errors in a database while keeping file bytes in Blob.
- Move long-running work to a queue: Use durable workflows for retries, page-level OCR, AI enrichment, cleanup, and progress tracking.
- Support scanned PDFs: Detect pages without embedded text, render them as images, and process them with Tesseract.
- Add production controls: Introduce authentication, authorization, rate limits, analytics, tracing, privacy-safe logs, and retention policies.
Copy link to headingTroubleshooting
Copy link to headingOCR fails with “tesseract is not installed”
Cause: pytesseract is installed, but the operating-system executable is absent or not on PATH.
Fix: build from apps/api/Dockerfile.vercel, confirm tesseract-ocr is in the apt-get install list, and run tesseract --version inside the image when debugging locally.
Copy link to headingOCR cannot load a language
Cause: The requested trained-data file is not installed or Tesseract cannot find its tessdata directory.
Fix: Install the matching tesseract-ocr-<lang> package, confirm the language appears in tesseract --list-langs, and set TESSDATA_PREFIX only when using a nonstandard data location.
Copy link to headingThe container starts locally, but not on Vercel
Cause: The server may be bound to localhost, may ignore $PORT, or may exit during native dependency or model initialization.
Fix: Keep Uvicorn on 0.0.0.0:$PORT, inspect the service logs, and move expensive or failure-prone initialization out of module import when appropriate.
Copy link to heading/api/extract returns the Next.js page or a 404
Cause: The repository was not deployed as a Services project, the rewrite is missing, or the FastAPI route does not include the /api prefix.
Fix: Deploy from the repository root, select the Services framework setting, keep the top-level rewrite, and verify /api/health before testing uploads.
Copy link to headingAn image is small on disk but exhausts memory
Cause: Compressed file size does not represent decoded pixel memory. Very large dimensions or concurrent image copies can consume far more memory.
Fix: Keep Pillow's decompression-bomb protection, enforce pixel and page limits, resize only when safe, and measure peak memory with representative documents.
Copy link to headingBuilds become slow after adding a model
Cause: Weights are being downloaded after the application code is copied, or an unversioned asset invalidates the image cache frequently.
Fix: Place stable model acquisition in a dependency layer, pin and checksum the asset, use a multi-stage build when needed, and reconsider remote inference if the model dominates the image.
Copy link to headingRelated resources
- Running Docker on Vercel
- Does Vercel support Docker deployments?
- The complete guide to Vercel Services
- Using the Python runtime with Vercel Functions
- Tesseract documentation
Copy link to headingFAQ
What is the container image size limit?
The standard Function path allows a 250 MB uncompressed package, while the native Python runtime has a 500 MB standard allowance. The Large Functions public beta supports Node.js, Python, and container images up to 5 GB uncompressed on Fluid compute.
How long can the container process one request?
Container services inherit Vercel Function duration limits. On Fluid compute, the current documented default is 300 seconds. Hobby Functions can run for up to 300 seconds, while the standard maximum for Pro and Enterprise is 1800 seconds.
Can I use WebSockets with a Docker service?
Yes, with current limitations. WebSocket support for Vercel Functions is in public beta on Fluid compute, including container-image Functions. A connection stays pinned to one Function instance and is limited by that Function's maximum duration, so clients must reconnect.
Can I deploy the frontend and API together?
Yes. Vercel Services can build the Next.js and container services independently, route both through one public URL, and create a matching set of services for every preview deployment.
How large can an uploaded document be?
Vercel Functions accept request and response bodies up to 4.5 MB. This template sets its own limit to 4 MB in the browser and FastAPI service, leaving room for multipart form metadata. Files larger than the application limit receive a 413 response.
For larger documents, upload directly from the browser to Vercel Blob and send only the resulting object reference to the API. This keeps the file bytes out of the Function request body. Vercel recommends Blob client uploads for this pattern.