Global • Group • Private messaging — built on Django + WebSockets with a production-ready ASGI architecture.
- ✨ Overview
- 🚀 Why Pinggo?
- 🧩 Features
- 🏗 System Design
- 🛠 Tech Stack
- 📦 Project Structure
- ⚙️ Getting Started
- 🌩 Environment Variables
- 🐳 Docker Usage
- 🚢 Production Deployment
- 🧪 Security & Best Practices
- 🗺 Roadmap
- 🤝 Contributing
- 👨💻 Author
- 📄 License
Pinggo is a real-time chat platform designed to deliver smooth, instant messaging across:
- 🌍 Global chat
- 👥 Group rooms
- 🔐 Private 1:1 DMs
- 🖼 Media sharing (Cloudinary)
It’s built like a deployable product: ASGI-first, Redis channel layer, PostgreSQL storage, and a modern UI stack for responsive interactions.
Live: http://143.244.132.69/
- Realtime-first: WebSocket messaging using Django Channels.
- Scales horizontally: Redis/Upstash for the channel layer fan-out.
- Production-ready: ASGI + Daphne/Uvicorn compatible deployment.
- Cloud media: Cloudinary for avatars and attachments.
- Modern UI: Tailwind + HTMX + Alpine.js for app-like experience.
- ✅ Real-time messaging via WebSockets
- ✅ Global / Group / Private rooms
- ✅ Room-based broadcast delivery
- ✅ Persistent history in PostgreSQL
- ✅ User avatars
- ✅ Group avatars
- ✅ File uploads / attachments
- ✅ Cloudinary integration
- ✅ ASGI deployment support
- ✅ Redis channel layer for scale
- ✅ Clear separation of routes/consumers/services
flowchart TB
U[Users / Browsers] -->|HTTPS| LB[Load Balancer / Reverse Proxy<br/>Nginx / Cloud LB]
U -->|WSS WebSocket Secure| LB
LB --> APP[ASGI App<br/>Django + Channels<br/>Daphne/Uvicorn Workers]
APP --> DB[(PostgreSQL<br/>Users, Rooms, Messages, Memberships)]
APP --> REDIS[(Redis / Upstash<br/>Channel Layer + PubSub)]
APP --> CLOUD[(Cloudinary<br/>Avatars + Files)]
APP --> EMAIL[Email Provider / API<br/>Verification / system emails]
flowchart LR
subgraph Client["Client (Browser)"]
UI[Chat UI<br/>Tailwind/HTMX/Alpine]
WS[WebSocket Client]
HTTP[HTTP Client]
UI --> WS
UI --> HTTP
end
subgraph Server["Server (Django ASGI)"]
ROUTES[HTTP Routes<br/>Auth, Rooms, Profiles]
CONSUMERS[Channels Consumers<br/>WebSocket handlers]
AUTH[Auth Layer<br/>Sessions/JWT/Allauth]
SVC[Service Layer<br/>RoomService, MessageService]
POL[Permissions / Policy]
SERIAL[Serialization / Validation]
end
subgraph Storage[Storage & Infra]
PG[(PostgreSQL)]
REDIS[(Redis Channel Layer)]
CLOUD[(Cloudinary)]
end
HTTP --> ROUTES
WS --> CONSUMERS
ROUTES --> AUTH
CONSUMERS --> AUTH
AUTH --> POL
ROUTES --> SERIAL
CONSUMERS --> SERIAL
SERIAL --> SVC
POL --> SVC
SVC --> PG
CONSUMERS --> REDIS
SVC --> CLOUD
sequenceDiagram
autonumber
participant C as Client (Browser)
participant LB as LB / Nginx
participant A as ASGI App (Django+Channels)
participant R as Redis (Channel Layer)
participant P as PostgreSQL
C->>LB: WSS connect /ws/chat/{room}/
LB->>A: Upgrade to WebSocket
A->>R: Join group (room channel group)
C->>A: Send message payload (WS frame)
A->>A: Validate + authorize (membership/permissions)
A->>P: Persist message (insert)
A->>R: Publish to room group
R-->>A: Fan-out event to all subscribers
A-->>C: Broadcast message event (WS)
sequenceDiagram
autonumber
participant C as Client
participant A as ASGI App
participant CL as Cloudinary
participant P as PostgreSQL
C->>A: Request upload (HTTP) / get upload signature
A->>A: Check auth + size/type rules
A-->>C: Return signed upload params (or upload endpoint)
C->>CL: Upload file (direct to Cloudinary)
CL-->>C: Return asset URL + public_id
C->>A: Send message referencing asset URL (WS/HTTP)
A->>P: Save message with attachment metadata
A-->>C: Broadcast message with attachment URL
sequenceDiagram
autonumber
participant C as Client
participant A as ASGI App
participant P as PostgreSQL
participant R as Redis
C->>A: Create/find DM room (HTTP)
A->>P: Upsert DM room + membership (two users)
A-->>C: Return room_id
C->>A: WSS connect /ws/chat/{room_id}/
A->>R: Join DM group
C->>A: Send message (WS)
A->>P: Persist message
A->>R: Publish to DM group
A-->>C: Broadcast to both users
flowchart TB
U[Users] --> LB[Cloud LB / Nginx]
LB --> A1[ASGI Instance #1<br/>Daphne/Uvicorn + Django]
LB --> A2[ASGI Instance #2<br/>Daphne/Uvicorn + Django]
LB --> A3[ASGI Instance #3<br/>Daphne/Uvicorn + Django]
A1 --> REDIS[(Redis / Upstash<br/>Channel Layer)]
A2 --> REDIS
A3 --> REDIS
A1 --> PG[(PostgreSQL)]
A2 --> PG
A3 --> PG
A1 --> CLOUD[(Cloudinary)]
A2 --> CLOUD
A3 --> CLOUD
erDiagram
%% USERS
USER ||--|| PROFILE : has_profile
%% CHAT GROUPS
USER ||--o{ CHATGROUP : creates
USER }o--o{ CHATGROUP : member_of
%% MESSAGES
CHATGROUP ||--o{ GROUPMESSAGE : has
USER ||--o{ GROUPMESSAGE : writes
PROFILE {
int id
int user_id
string image_url
string displayname
string info
}
CHATGROUP {
int id
string group_name
string chat_type "global|group|private"
string description
string image_url
int creator_id
datetime created_at
}
GROUPMESSAGE {
int id
int group_id
int author_id
string message
string file_url
string file_type "image|video|audio|pdf|other"
string file_name
datetime created_at
}
- Django
- Django Channels (WebSockets)
- ASGI + Daphne/Uvicorn
- PostgreSQL
- Redis / Upstash Redis
- HTML5
- Tailwind CSS
- HTMX
- Alpine.js
- JavaScript
- Cloudinary
Pinggo/
│
├── chats/ # Chat related files and configuration
├── home/ # Simple app for redirection
├── MailApix/ # app for configuring MailAPIX API as an email backed
├── Pinggo/ # Django project core
├── static/ # Static files folder
├── templates/ # Html files folder
├── users/ # app for user authentication and authorization
├── __init__.py
├── manage.py
├── .env
└── requirements.txt
git clone https://github.com/Sumit0ubey/Pinggo.git
cd Pinggo
python -m venv venv
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate
pip install -r requirements.txtpython manage.py makemigrations
python manage.py migratepython manage.py runserverCreate .env based on .env.example.
DEBUG=True
SECRET_KEY=your_secret_key
APP_NAME=Pinggo
# Database
DATABASE_HOST=
DATABASE_PORT=
DATABASE_NAME=
DATABASE_USER=
# Redis / Upstash
UPSTASH_REDIS_URL=
# Cloudinary
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
# Optional folders
CLOUDINARY_USER_AVATAR=
CLOUDINARY_GROUP_AVATAR=
CLOUDINARY_CHAT_FILES=If you want to run Pinggo directly with containers, use the compose setup in Pinggo/docker-compose.yml.
In the Pinggo/ directory, create .env from Pinggo/.envDockerExample, then update all required values before starting containers.
cd Pinggo
# Linux/macOS
cp .envDockerExample .env
# Windows PowerShell
Copy-Item .envDockerExample .envThe current Pinggo/docker-compose.yml starts four services:
db-> PostgreSQL 15 (persistent data inpostgres_data)redis-> Redis 7 (persistent data inredis_data)web-> Pinggo app container (currently uses imagedubeysumit/pinggo:v.1.5)nginx-> reverse proxy on port80
So right now, by default, your app runs from the already published image in compose.
cd Pinggo
docker compose up --buildIf your system uses the legacy command, you can use:
docker-compose up --buildPinggo/Dockerfile is already set up for production-style startup (collectstatic, migrate, then Daphne).
To build your own image from this Dockerfile:
cd Pinggo
docker build -t your-dockerhub-username/pinggo:latest .Optional push to your own registry repo:
docker login
docker push your-dockerhub-username/pinggo:latestIn Pinggo/docker-compose.yml, update services.web.image.
Current:
web:
image: dubeysumit/pinggo:v.1.5Replace with your image:
web:
image: your-dockerhub-username/pinggo:latestThen run compose again:
cd Pinggo
docker compose up -dIf you do not want to pull any registry image, you can make compose build locally from Pinggo/Dockerfile.
Example web service:
web:
build:
context: .
dockerfile: Dockerfile
image: pinggo:localThen start:
cd Pinggo
docker compose up --buildcd Pinggo
# Stop containers
docker compose down
# Run in detached mode
docker compose up -ddaphne -b 0.0.0.0 -p 8000 Pinggo.asgi:application- Nginx reverse proxy (TLS termination)
- PostgreSQL managed DB
- Upstash Redis (channel layer)
- Cloudinary for media
DEBUG=Falseand restrictedALLOWED_HOSTS
- ✅ Keep secrets in environment variables
- ✅ Use
DEBUG=Falsein production - ✅ Validate message payloads server-side
- ✅ Add rate limiting (WS + HTTP) for abuse prevention
- ✅ Restrict allowed file types/sizes before upload
- ✅ Use HTTPS/WSS everywhere
- Read receipts ✅/👀
- Typing indicators ✍️
- Reactions ❤️🔥
- Push notifications 🔔
- Search 🔎
- Moderation tools 🛡
- Optional E2E encryption 🔐
- Fork the repo
- Create a branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m "Add amazing feature" - Push:
git push origin feature/amazing-feature - Open a PR 🎉
Sumit Dubey
GitHub: https://github.com/Sumit0ubey
This project is licensed under the MIT License.
See the LICENSE file for details.