Readdig is an RSS and Podcast reader application.
- RSS feed reader
- Podcast player
- User authentication and management
- Feed organization with folders and tags
- Article starring and reading history
- OPML import/export
- Email notifications
- Payment integration with Paddle
The application consists of:
- API: Node.js backend service (Express.js)
- App: React frontend (Create React App)
- Database: PostgreSQL
- Cache: Redis
- Queue: Bull (Redis-based)
- Node.js 18.20.8 or later
- PostgreSQL 12 or later
- Redis 6 or later
- Docker and Docker Compose (for Docker deployment)
git clone https://github.com/readdig/readdig.git
cd readdigcd api
# Install dependencies
yarn install
# Configure environment
cp .env.example .env
# Edit .env with your settings
# Run database migrations
yarn db:migrate
# Start development server
yarn devThe API will be available at http://localhost:8000
cd ../app
# Install dependencies
yarn install
# Configure environment
cp .env.example .env
# Edit .env with your settings
# Start development server
yarn startThe app will be available at http://localhost:3000
git clone <your-repository-url>
cd readdigcd api
# Copy configuration templates
cp .env.example .env
cp docker-compose.yml.example docker-compose.yml
cp ecosystem.prod.config.js.example ecosystem.prod.config.jsEdit api/.env:
# Production environment
NODE_ENV=production
# Product Information
PRODUCT_URL=https://www.readdig.com
PRODUCT_NAME=Readdig.com
USER_AGENT=ReaddigBot/1.0 (https://www.readdig.com)
# Security - IMPORTANT: Generate secure keys
JWT_SECRET=your-jwt-secret-key-here
# Email Configuration (SendGrid)
EMAIL_SENDER_SUPPORT_NAME=Readdig Support
[email protected]
EMAIL_SENDGRID_SECRET=your-sendgrid-api-key
# Optional: Cloudflare Worker Proxy
CLOUDFLARE_PROXY_URL=https://your-worker.workers.dev
CLOUDFLARE_PROXY_SECRET=your-cloudflare-secret
# Optional: Paddle Payment Integration
PADDLE_PUBLIC_KEY=your-paddle-public-key
PADDLE_API_URL=https://vendors.paddle.com/api/2.0
PADDLE_VENDOR_ID=your-vendor-id
PADDLE_VENDOR_AUTH_CODE=your-auth-code
# Optional: Sentry Error Tracking
SENTRY_DSN=your-sentry-dsnEdit api/docker-compose.yml to set a secure database password:
api:
environment:
DATABASE_URL: postgresql://readdig:your-secure-password@database:5432/readdig
database:
environment:
POSTGRES_PASSWORD: your-secure-passwordImportant: Make sure the database password in database.environment.POSTGRES_PASSWORD matches the one in api.environment.DATABASE_URL
Note: Redis runs without password in the internal Docker network, which is secure for private deployments.
# Build and start all services (API, PostgreSQL, Redis)
docker-compose up -d
# Check service status
docker-compose ps
# View logs
docker-compose logs -f apiThe API will be available at http://localhost:8000
Note: Database migrations will run automatically when the API container starts.
cd ../app
# Copy configuration templates
cp .env.example .env
cp docker-compose.yml.example docker-compose.yml
cp nginx.conf.example nginx.confEdit app/.env:
# React App Configuration
NODE_ENV=production
# Product Information
REACT_APP_PRODUCT_URL=https://www.readdig.com
REACT_APP_PRODUCT_NAME=Readdig.com
REACT_APP_PRODUCT_DESCRIPTION=Readdig.com an RSS and Podcast reader
# API Endpoint - IMPORTANT: Point to your actual API
REACT_APP_API_URL=https://api.readdig.com
# Or if API is on same domain: https://www.readdig.com/api
# Optional: Analytics and Monitoring
REACT_APP_SENTRY_DSN=your-sentry-dsn
REACT_APP_PADDLE_VENDOR_ID=your-paddle-vendor-idEdit app/nginx.conf (replace domain names):
server {
listen 80;
listen [::]:80;
server_name yourdomain.com;
return 301 http://www.yourdomain.com$request_uri;
}
server {
listen 80;
listen [::]:80;
server_name www.yourdomain.com;
# Serve React app static files
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html =404;
}
# Proxy API requests to backend
location /api/ {
proxy_pass http://api:8000/;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}
}Notes:
- This is the Nginx configuration inside the app Docker container
- Replace
yourdomain.comwith your actual domain name - The first server block redirects non-www to www
proxy_pass http://api:8000/- points to the API container (using Docker network name)- The trailing slash in
/api/andhttp://api:8000/is important - it removes/apiprefix when forwarding - For production deployments with external reverse proxy, you may need to adjust this configuration
# Build the app (compiles React with environment variables)
docker-compose build
# Start the app service
docker-compose up -d
# View logs
docker-compose logs -f appThe frontend will be available at http://localhost:80
# Check API health
curl http://localhost:8000/health
# Expected response: {"status":"ok"}Open your browser:
- Frontend:
http://localhost:80 - API:
http://localhost:8000
| Variable | Description | Required |
|---|---|---|
NODE_ENV |
Environment (development/production) | Yes |
API_PORT |
API server port | Yes |
API_HOST |
API server host | Yes |
DATABASE_URL |
PostgreSQL connection string | Yes |
CACHE_URL |
Redis connection string | Yes |
JWT_SECRET |
JWT signing secret | Yes |
EMAIL_SENDGRID_SECRET |
SendGrid API key | No |
SENTRY_DSN |
Sentry error tracking DSN | No |
PADDLE_VENDOR_ID |
Paddle vendor ID | No |
| Variable | Description | Required |
|---|---|---|
REACT_APP_PRODUCT_URL |
Product URL | Yes |
REACT_APP_PRODUCT_NAME |
Product name | Yes |
REACT_APP_API_URL |
API endpoint URL | Yes |
REACT_APP_SENTRY_DSN |
Sentry DSN | No |
REACT_APP_PADDLE_VENDOR_ID |
Paddle vendor ID | No |
For production, use a reverse proxy Nginx on your host to:
- Handle SSL/TLS certificates
- Route requests to appropriate services
- Serve both app and API from the same domain
Important: This is the host-level reverse proxy configuration, separate from the nginx.conf inside the app Docker container. The architecture is:
Internet → Host Nginx (SSL/proxy) → Docker Containers
├─ App container (port 80)
└─ API container (port 8000)
Configuration Summary:
| Configuration | Location | Purpose |
|---|---|---|
app/nginx.conf |
Inside app Docker container | Serves React static files and proxies /api/ to backend container |
Deployment scenarios:
- Local development: Only
app/nginx.confneeded (no SSL) - Production with Docker only: Only
app/nginx.confneeded (add SSL to Docker config) - Production with host reverse proxy (recommended): Both configs needed - host Nginx handles SSL,
app/nginx.confhandles internal routing
# API logs
cd api && docker-compose logs -f api
# App logs
cd app && docker-compose logs -f app
# Database logs
cd api && docker-compose logs -f database# Restart API
cd api && docker-compose restart api
# Restart App
cd app && docker-compose restart app# Pull latest code
git pull
# Update API
cd api
docker-compose down
docker-compose build --no-cache
docker-compose up -d
# Update App (requires rebuild when env vars change)
cd ../app
docker-compose down
docker-compose build --no-cache
docker-compose up -d# Create backup
docker exec database pg_dump -U readdig readdig > backup_$(date +%Y%m%d_%H%M%S).sql
# Restore backup
docker exec -i database psql -U readdig readdig < backup_20231201_120000.sql# View resource usage
docker stats
# View disk usage
docker system df-
Check database is running:
docker-compose ps database
-
Verify
DATABASE_URLin.env:DATABASE_URL=postgresql://readdig:password@database:5432/readdig
-
Check database logs:
docker-compose logs database
- Verify
REACT_APP_API_URLinapp/.env - Rebuild the app (React env vars are set at build time):
docker-compose build --no-cache docker-compose up -d
Change the port mapping in docker-compose.yml:
ports:
- '8080:8000' # Use port 8080 instead of 8000Clean up Docker resources:
# Remove unused images
docker image prune -a
# Remove unused volumes
docker volume prune
# Clean everything
docker system prune -a --volumesFor API: Restart the container after changing .env:
docker-compose restart apiFor App: Rebuild the image (React bakes env vars at build time):
docker-compose build --no-cache
docker-compose up -d- Change Default Passwords: Update database password in both
docker-compose.ymland.env - Use Strong JWT Secret: Generate a secure random string for
JWT_SECRET - Enable HTTPS: Use a reverse proxy with SSL/TLS certificates
- Restrict Ports: Only expose necessary ports to the public
- Regular Updates: Keep Docker images and dependencies updated
- Environment Variables: Never commit
.envfiles to version control - Database Backups: Set up automated database backups
yarn api # Start API server
yarn conductor # Start conductor worker
yarn feed # Start feed worker
yarn og # Start OG worker
yarn clean # Start clean worker
yarn dev # Start all services with PM2
yarn build # Build for production
yarn db:migrate # Run database migrations
yarn db:studio # Open Drizzle Studioyarn start # Start development server
yarn build # Build for productionIf you find this project helpful, consider supporting the development: