A production-ready FastAPI server for receiving, validating, and storing GitLab webhook events in PostgreSQL. This server provides endpoints for core GitLab webhook events (Push, Merge Request, and Issue) with an extensible architecture for adding more event types.
- ✅ Core GitLab Webhooks: Push, Merge Request, and Issue events
- ✅ PostgreSQL Storage: Persistent storage of all webhook events with JSONB payloads
- ✅ Token Authentication: Secure webhook verification using GitLab secret tokens
- ✅ Automatic API Documentation: Interactive Swagger UI and ReDoc
- ✅ Health Check Endpoint: Monitor server and database status
- ✅ Docker Support: Easy deployment with Docker Compose
- ✅ Database Migrations: Version-controlled schema changes with Alembic
- ✅ Extensible Architecture: Easy to add new webhook event types
- ✅ Comprehensive Logging: Structured logging for debugging and monitoring
- ✅ Type Safety: Full Pydantic validation for webhook payloads
- Python 3.11+
- PostgreSQL 15+
- Docker & Docker Compose (optional, for containerized deployment)
- Clone the repository
git clone <repository-url>
cd gitlab-webhook-server- Set up environment
cp .env.example .env
# Edit .env with your configuration- Install dependencies
pip install -r requirements.txt- Start PostgreSQL (using Docker)
docker-compose up -d postgres- Initialize database
alembic upgrade head- Run the server
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000The server will be available at:
- API: http://localhost:8000
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
For a complete setup with PostgreSQL:
docker-compose up --buildThis will start both the FastAPI application and PostgreSQL database.
Create a .env file with the following variables:
# Database Configuration
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/gitlab_webhooks
# GitLab Configuration
GITLAB_WEBHOOK_SECRET=your-secret-token-here
# Application Configuration
APP_NAME=GitLab Webhook Server
APP_VERSION=1.0.0
LOG_LEVEL=INFO
# CORS Configuration
CORS_ORIGINS=["*"]| Endpoint | Method | Description | GitLab Event |
|---|---|---|---|
/webhooks/push |
POST | Handle push events | Push Hook |
/webhooks/merge-request |
POST | Handle merge request events | Merge Request Hook |
/webhooks/issue |
POST | Handle issue events | Issue Hook |
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check endpoint |
/ |
GET | API information |
/docs |
GET | Swagger UI documentation |
/redoc |
GET | ReDoc documentation |
- Navigate to your GitLab project
- Go to Settings > Webhooks
- Add webhook URL:
http://your-server:8000/webhooks/{event-type} - Enter your secret token (must match
GITLAB_WEBHOOK_SECRET) - Select trigger events:
- Push events →
/webhooks/push - Merge request events →
/webhooks/merge-request - Issues events →
/webhooks/issue
- Push events →
- Click Add webhook
URL: http://your-server.com:8000/webhooks/push
Secret Token: your-secret-token-here
Trigger: Push events
SSL verification: Enable (recommended for production)
Test the push webhook:
curl -X POST http://localhost:8000/webhooks/push \
-H "Content-Type: application/json" \
-H "X-Gitlab-Token: your-secret-token" \
-H "X-Gitlab-Event: Push Hook" \
-d @test_payloads/push_event.jsonExpected response:
{
"status": "success",
"message": "Push event received and stored",
"event_id": "550e8400-e29b-41d4-a716-446655440000"
}- Navigate to http://localhost:8000/docs
- Click on an endpoint to expand it
- Click "Try it out"
- Add the
X-Gitlab-Tokenheader - Paste a webhook payload
- Click "Execute"
See WEBHOOK_EXAMPLES.md for complete example payloads.
The server uses a single table to store all webhook events:
CREATE TABLE webhook_events (
id UUID PRIMARY KEY,
event_type VARCHAR(50) NOT NULL,
event_name VARCHAR(100) NOT NULL,
project_id INTEGER,
project_name VARCHAR(255),
payload JSONB NOT NULL,
headers JSONB,
received_at TIMESTAMP NOT NULL,
processed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP NOT NULL
);
CREATE INDEX idx_event_type ON webhook_events(event_type);
CREATE INDEX idx_project_id ON webhook_events(project_id);
CREATE INDEX idx_created_at ON webhook_events(created_at);-- View recent webhooks
SELECT id, event_type, project_name, created_at
FROM webhook_events
ORDER BY created_at DESC
LIMIT 10;
-- Count events by type
SELECT event_type, COUNT(*)
FROM webhook_events
GROUP BY event_type;
-- Get specific event details
SELECT payload
FROM webhook_events
WHERE id = 'your-event-id';
-- Find all push events for a project
SELECT *
FROM webhook_events
WHERE event_type = 'push'
AND project_id = 15;gitlab-webhook-server/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI application
│ ├── config.py # Configuration management
│ ├── database/
│ │ ├── __init__.py
│ │ ├── connection.py # Database setup
│ │ └── models.py # SQLAlchemy models
│ ├── routers/
│ │ ├── __init__.py
│ │ ├── webhooks.py # Webhook endpoints
│ │ └── health.py # Health check
│ ├── schemas/
│ │ ├── __init__.py
│ │ ├── push.py # Push event schema
│ │ ├── merge_request.py # MR event schema
│ │ └── issue.py # Issue event schema
│ └── utils/
│ ├── __init__.py
│ ├── auth.py # Token verification
│ └── logging.py # Logging config
├── alembic/ # Database migrations
├── tests/ # Test files
├── .env.example # Environment template
├── docker-compose.yml # Docker setup
├── Dockerfile # Container image
├── requirements.txt # Python dependencies
├── ARCHITECTURE.md # Architecture documentation
├── IMPLEMENTATION_GUIDE.md # Detailed implementation guide
├── WEBHOOK_EXAMPLES.md # Example webhook payloads
└── README.md # This file
The server follows a clean, modular architecture:
graph TB
GL[GitLab Server] -->|Webhook POST| API[FastAPI Server]
API -->|Validate Token| AUTH[Auth Middleware]
AUTH -->|Route Event| ROUTER[Webhook Router]
ROUTER -->|Parse & Store| DB[(PostgreSQL)]
API -->|Health Check| HEALTH[Health Endpoint]
API -->|Auto Docs| DOCS[Swagger/ReDoc]
For detailed architecture information, see ARCHITECTURE.md.
The architecture is designed for easy extensibility. To add a new webhook type:
- Create Pydantic schema in
app/schemas/new_event.py - Add endpoint in
app/routers/webhooks.py:
@router.post("/new-event", status_code=status.HTTP_200_OK)
async def handle_new_event(
request: Request,
event: NewEventSchema,
db: Session = Depends(get_db),
token: str = Depends(verify_gitlab_token)
):
webhook_event = WebhookEvent(
event_type="new_event",
event_name=event.event_name,
project_id=event.project.id,
project_name=event.project.name,
payload=event.model_dump(),
headers=dict(request.headers),
received_at=datetime.utcnow()
)
db.add(webhook_event)
db.commit()
return {"status": "success", "event_id": str(webhook_event.id)}- Configure in GitLab with URL:
/webhooks/new-event
No database migration needed! The JSONB payload field stores any event structure.
All webhook endpoints require a valid X-Gitlab-Token header that matches the configured secret:
X-Gitlab-Token: your-secret-token- Use HTTPS: Always use TLS/SSL in production
- Strong Secrets: Generate cryptographically secure tokens
- Network Security: Restrict access to webhook endpoints
- Rate Limiting: Implement rate limiting for webhook endpoints
- Monitoring: Set up alerts for failed authentications
- Database Security: Use strong database credentials
- Environment Variables: Never commit secrets to version control
Monitor server health:
curl http://localhost:8000/healthResponse:
{
"status": "ok",
"timestamp": "2024-01-01T12:00:00.000000",
"database": "healthy"
}The server provides structured logging:
2024-01-01 12:00:00 - app.routers.webhooks - INFO - Received push event for project: MyProject
2024-01-01 12:00:00 - app.routers.webhooks - INFO - Stored push event with ID: 550e8400-e29b-41d4-a716-446655440000
Configure log level in .env:
LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR, CRITICALIssue: Connection refused when connecting to PostgreSQL
Solution: Ensure PostgreSQL is running and the DATABASE_URL is correct
docker-compose up -d postgresIssue: 401 Unauthorized when testing webhooks
Solution: Verify the X-Gitlab-Token header matches GITLAB_WEBHOOK_SECRET
Issue: 422 Validation Error when receiving webhooks
Solution: Check the webhook payload structure matches the Pydantic schema. Enable debug logging to see the full error.
Issue: Database migration fails
Solution: Reset the database and run migrations again:
alembic downgrade base
alembic upgrade headpytest tests/ -vCreate a new migration:
alembic revision --autogenerate -m "Description of changes"Apply migrations:
alembic upgrade headRollback migration:
alembic downgrade -1The project follows PEP 8 style guidelines. Format code with:
black app/
isort app/- Connection Pooling: SQLAlchemy connection pool (10 connections, 20 max overflow)
- Async Operations: FastAPI async endpoints for better concurrency
- Database Indexes: Optimized queries on
event_type,project_id, andcreated_at - JSONB Storage: Efficient storage and querying of webhook payloads
Build and deploy with Docker:
docker build -t gitlab-webhook-server .
docker run -p 8000:8000 --env-file .env gitlab-webhook-serverFor production deployments:
- Use a managed PostgreSQL service (AWS RDS, Google Cloud SQL)
- Deploy behind a reverse proxy (Nginx, Traefik)
- Enable HTTPS with valid SSL certificates
- Set up monitoring and alerting
- Configure log aggregation
- Implement backup strategies
- Use container orchestration (Kubernetes, Docker Swarm)
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
[Your License Here]
For issues and questions:
- GitHub Issues: [Your Repository URL]
- Documentation: See
ARCHITECTURE.mdandIMPLEMENTATION_GUIDE.md - Examples: See
WEBHOOK_EXAMPLES.md
Future enhancements:
- Add remaining GitLab webhook types (Pipeline, Deployment, Wiki, etc.)
- Implement webhook replay functionality
- Add webhook filtering and routing rules
- Create admin dashboard for webhook management
- Add webhook event streaming (WebSocket/SSE)
- Implement retry mechanism for failed processing
- Add HMAC signature verification
- Create webhook analytics and reporting
- Add GraphQL API for querying webhook events
- Implement webhook event archival strategy
Built with:
- FastAPI - Modern web framework
- SQLAlchemy - SQL toolkit and ORM
- Pydantic - Data validation
- Alembic - Database migrations
- PostgreSQL - Database system