Building an Async Processing System with FastAPI, Celery and RabbitMQ on Windows
While building scalable systems, long running tasks (OCR, ML, file processing) should never block your API.
Get Shaik Sabiha Tarannum’s stories in your inbox
Join Medium for free to get updates from this writer.
That’s where:
- FastAPI- handles requests
- RabbitMQ- queues tasks
- Celery- processes tasks asynchronously
Initial Setup Guide
- Install Dependencies
Note: It is strongly recommended to use Python 3.11 for this setup. Newer versions such as Python 3.14 may introduce compatibility issues with libraries like Celery and asyncio, and are not yet considered stable for this use case.
2. Configure Environment Variables
Add the following path to your system’s Environment variables (Path)-
C:\Program Files\RabbitMQ Server\rabbitmq_server-4.2.5\sbi3. Enable RabbitMQ management plugin
Open command prompt as administrator and run the following command
rabbitmq-plugins enable rabbitmq_management4. Restart RabbitMQ service
Run the following commands as administrator
net stop RabbitMQnet start RabbitMQ5. Access RabbitMQ management dashboard
Open the browser and navigate to-
http://localhost:15672/Default login credentials
- Username- guest
- Password- guest
Note-
- RabbitMQ uses port 5672 for application-level communication (used by Celery and other clients).
- The RabbitMQ Management Dashboard runs on port 15672 (accessible via browser). You can create a new user from the Admin tab → Add user.
- Celery workers do not expose any ports — they communicate with RabbitMQ through the broker URL (typically via port 5672).
- Celery is a worker (consumer), not a server. It does not listen for incoming requests
Project Structure
invoice-demo/
│
├── app.py
├── requirements.txt
├── local_run.bat
├── .env.stage
│
├── config/
│ └── celery_app.py
│
├── controllers/
│ └── task_controller.py
│
├── services/
│ └── task_service.py- Environment File
.env.stageENV=stage
ALLOWED_ORIGINS=["http://localhost:5173"] #If UI is React + Vite
CELERY_BROKER_URL=amqp://guest:guest@localhost:5672//
CELERY_RESULT_BACKEND=rpc://2. Celery Configuration
config/celery_app.pyfrom celery import Celery
import os
from dotenv import load_dotenv
env = os.getenv("ENV", "stage")
load_dotenv(f".env.{env}")
celery = Celery(
"demo_tasks",
broker=os.getenv("CELERY_BROKER_URL"),
backend=os.getenv("CELERY_RESULT_BACKEND"),
)
celery.conf.update(
task_serializer="json",
accept_content=["json"],
result_serializer="json",
timezone="UTC",
enable_utc=True,
imports=["services.task_service"]
)3. Celery Task (Service Layer)
services/task_service.pyfrom config.celery_app import celery
import time
@celery.task(bind=True)
def long_running_task(self, number: int):
print(f"Processing number: {number}")
# simulate heavy work
time.sleep(5)
result = number * number
print(f"Completed: {result}")
return {"input": number, "result": result}4. Controller (API Layer)
controllers/task_controller.pyfrom fastapi import APIRouter
from services.task_service import long_running_task
router = APIRouter(prefix="/tasks", tags=["Tasks"])
@router.post("/process")
async def process_number(number: int):
task = long_running_task.delay(number)
return {
"message": "Task submitted",
"task_id": task.id
}5. FastAPI App
app.pyimport uvicorn
import os
import json
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from controllers.task_controller import router as task_router
origins_str = os.getenv("ALLOWED_ORIGINS")
try:
ALLOWED_ORIGINS = json.loads(origins_str) if origins_str else ["*"]
except Exception:
ALLOWED_ORIGINS = ["*"]
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(task_router)
if __name__ == "__main__":
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)7. requirements.txt
fastapi # Web framework to build APIs (handles routing, validation, request/response)
uvicorn[standard] # ASGI server to run FastAPI app
python-dotenv # Loads environment variables from .env.stage file into the app
celery # Distributed task queue to run background/async jobs (like OCR processing)
kombu # Messaging library used internally by Celery to communicate with brokers (RabbitMQ)
amqp # Low-level AMQP protocol client used by kombu to talk to RabbitMQ8. local_run.bat
@echo on
SETLOCAL
echo ==========================================
echo LOCAL RUN - FASTAPI + CELERY + RABBITMQ
echo ==========================================
cd /d %~dp0
set ENV=stage
REM ==========================================
REM STEP 1: Start RabbitMQ
REM ==========================================
echo Checking RabbitMQ...
sc query RabbitMQ | findstr /I "RUNNING" >nul
IF ERRORLEVEL 1 (
echo Starting RabbitMQ...
net start RabbitMQ >nul 2>&1
IF ERRORLEVEL 1 (
echo ERROR: Failed to start RabbitMQ!
pause
exit /b 1
)
) ELSE (
echo RabbitMQ already running.
)
timeout /t 5 >nul
REM ==========================================
REM STEP 2: Python ENV
REM ==========================================
if not exist ".venv" (
echo Creating virtual environment...
python -m venv .venv
)
call .venv\Scripts\activate.bat
echo Installing requirements...
pip install -r requirements.txt
REM ==========================================
REM STEP 3: Start Celery Worker
REM ==========================================
echo Starting Celery worker...
start cmd /k celery -A config.celery_app.celery worker --loglevel=info --pool=solo
timeout /t 3 >nul
REM ==========================================
REM STEP 4: Start FastAPI
REM ==========================================
echo Starting FastAPI...
uvicorn app:app --host 0.0.0.0 --port 8000 --reload
ENDLOCAL
pauseHow to Test
- Start everything
local_run.batThis will start the API and a terminal to check Celery logs will open like-
2. Open Swagger
http://localhost:8000/docs3. Call API
POST /tasks/process?number=54. Check Celery logs
Your setup is Ready !!!
P.S. If you’re interested in setting up the same architecture in a production environment — including IIS hosting, port configuration, and running RabbitMQ, Celery, and FastAPI as services — feel free to reach out.





