Sign inSign up

omairsalman/usersconnect

By omairsalman

•Updated about 1 month ago

Self-hosted social platform built with Node.js, Express and TypeScript

Image
1

4.6K

omairsalman/usersconnect repository overview

⁠UsersConnect

A modern self-hosted social media platform built with Node.js, Express, TypeScript, MySQL, and Redis. Features posts with optional images, comments, likes/dislikes, email verification, and a guided setup wizard.

GitHub: https://github.com/omairsalman03/UsersConnect⁠
Live Version: https://usersconnect.cloudomair.org/⁠


⁠✨ What's New in v1.2.0

  • šŸ”” Update Check - once a day, the app checks whether a newer version is available and tells admins. Only the product name and version are sent; the check-in service counts instances without storing IP addresses (source⁠). Disable with UPDATE_CHECK_ENABLED=false
  • šŸ›”ļø CSRF Protection - state-changing requests from other websites are now rejected
  • 🐳 Multi-Architecture Images - now published for amd64 and arm64 (Raspberry Pi, ARM NAS, Apple Silicon)
  • āš–ļø License Change - UsersConnect is now AGPL-3.0-only; 1.1.0 and earlier stay MIT (see License⁠)
  • šŸ”’ Security Patches - multer, nodemailer, mysql2 and qs updated

ā āš ļø HTTPS Is Required

As of v1.1.0, authentication cookies are always set with the Secure attribute (required by SameSite=None), so the app must be served over HTTPS for sign-in to work.

  • āœ… Standard deployments put the container behind a TLS-terminating reverse proxy (Nginx Proxy Manager, Nginx, Caddy, Traefik) — these already work, no action needed.
  • āŒ Plain-HTTP deployments will have authentication rejected by the browser — add HTTPS in front before upgrading.
  • šŸ§‘ā€šŸ’» Local development also needs HTTPS. http://localhost is treated as secure by Chrome/Firefox (so it works), but accessing from a phone over the LAN requires real TLS (e.g. mkcert or a tunnel). See "Why HTTPS is required"⁠ in the README.

ā šŸš€ Quick Start

⁠Step 1: Pull the Image
docker pull omairsalman/usersconnect:latest
⁠Step 2: Start with Docker Compose

Save as docker-compose.yml:

services:
  app:
    image: omairsalman/usersconnect:latest
    ports:
      - "3000:3000"
    volumes:
      - ./config.yaml:/app/config.yaml
      - ./logs:/app/logs
    environment:
      - NODE_ENV=production
      - DATABASE_HOST=mysql
      - DATABASE_USERNAME=root
      - DATABASE_PASSWORD=${DATABASE_PASSWORD}
      - DATABASE_NAME=usersconnect
      - REDIS_HOST=redis
      - REDIS_PASSWORD=${REDIS_PASSWORD}
      - ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
      - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
    depends_on:
      - mysql
      - redis
    restart: unless-stopped

  mysql:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD}
      MYSQL_DATABASE: usersconnect
    volumes:
      - mysql_data:/var/lib/mysql
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    restart: unless-stopped

volumes:
  mysql_data:
  redis_data:
docker-compose up -d

Generate secrets for .env:

openssl rand -base64 16  # For passwords
openssl rand -base64 32  # For JWT secrets
⁠Step 3: Complete Setup Wizard
  1. Visit your instance (redirects to /setup)
  2. Create your admin account
  3. Optionally configure:
    • S3 storage for image uploads
    • SMTP for email verification
    • CORS for separate frontends
  4. Click "Complete Setup"

Done! Your instance is ready to use.

Note: Because authentication requires HTTPS, access your instance through your HTTPS reverse proxy rather than plain http://...:3000. See the HTTPS note above.


⁠🐳 Docker Run (Alternative)

⁠Basic Setup
docker run -d \
  -p 3000:3000 \
  -v $(pwd)/config.yaml:/app/config.yaml \
  -v $(pwd)/logs:/app/logs \
  -e NODE_ENV=production \
  -e DATABASE_HOST=your-mysql-host \
  -e DATABASE_USERNAME=root \
  -e DATABASE_PASSWORD=yourpassword \
  -e DATABASE_NAME=usersconnect \
  -e REDIS_HOST=your-redis-host \
  -e REDIS_PASSWORD=yourredispassword \
  -e ACCESS_TOKEN_SECRET=$(openssl rand -base64 32) \
  -e REFRESH_TOKEN_SECRET=$(openssl rand -base64 32) \
  omairsalman/usersconnect:latest
⁠With S3 + SMTP

Add these environment variables:

  -e S3_ACCESS_KEY=your-aws-access-key \
  -e S3_SECRET_KEY=your-aws-secret-key \
  -e S3_BUCKET_NAME=usersconnect-media \
  -e S3_REGION=us-east-1 \
  -e SMTP_HOST=smtp.zoho.com \
  -e SMTP_PORT=465 \
  -e SMTP_SECURE=true \
  -e [email protected] \
  -e SMTP_PASSWORD=your_smtp_password \

ā šŸ”§ Environment Variables

⁠Required Configuration
Environment VariableYAML EquivalentDescriptionExample
DATABASE_HOSTdatabase.hostMySQL hostmysql or localhost
DATABASE_PORTdatabase.portMySQL port (optional)3306 (default)
DATABASE_USERNAMEdatabase.usernameMySQL userroot
DATABASE_PASSWORDdatabase.passwordMySQL passwordyourpassword
DATABASE_NAMEdatabase.nameDatabase nameusersconnect
REDIS_HOSTredis.hostRedis hostredis or localhost
REDIS_PORTredis.portRedis port (optional)6379 (default)
REDIS_PASSWORDredis.passwordRedis password (optional)yourredispassword
ACCESS_TOKEN_SECRETjwt.accessTokenSecretJWT access token secretGenerate: openssl rand -base64 32
REFRESH_TOKEN_SECRETjwt.refreshTokenSecretJWT refresh token secretGenerate: openssl rand -base64 32
⁠Optional: S3 Image Uploads

Image uploads are automatically enabled when these variables are set:

Environment VariableYAML EquivalentDescriptionExample
S3_ACCESS_KEYs3.accessKeyS3 access key IDAKIAIOSFODNN7EXAMPLE
S3_SECRET_KEYs3.secretKeyS3 secret access keywJalrXUtnFEMI/...
S3_BUCKET_NAMEs3.bucketNameS3 bucket nameusersconnect-media
S3_REGIONs3.regionAWS regionus-east-1
S3_ENDPOINTs3.endpointCustom endpoint (for non-AWS S3)https://s3.example.com
⁠Optional: SMTP Email

Email features are automatically enabled when these variables are set:

Environment VariableYAML EquivalentDescriptionExample
SMTP_HOSTsmtp.hostSMTP server hostsmtp.zoho.com
SMTP_PORTsmtp.portSMTP server port465 or 587
SMTP_SECUREsmtp.secureUse SSL/TLStrue or false
SMTP_USERsmtp.userSMTP username[email protected]
SMTP_PASSWORDsmtp.passwordSMTP passwordyour_smtp_password
⁠Optional: CORS Configuration

CORS is disabled by default. Enable for separate frontend applications:

Environment VariableYAML EquivalentDescriptionExample
CORS_ENABLEDcors.enabledEnable CORStrue or false
CORS_ALLOWED_ORIGINScors.allowedOriginsAllowed origins (comma-separated)http://localhost:4200,https://app.example.com
CORS_ALLOW_CREDENTIALScors.allowCredentialsAllow credentialstrue or false
CORS_ALLOWED_METHODScors.allowedMethodsAllowed HTTP methods (comma-separated)GET,POST,PUT,DELETE
CORS_ALLOWED_HEADERScors.allowedHeadersAllowed headers (comma-separated)Content-Type,Authorization
⁠Optional: Logging
Environment VariableYAML EquivalentDescriptionExample
LOG_LEVELlogging.levelWinston log levelinfo, debug, warn, error

Logging file location and rotation are configurable via logging.directory, logging.maxFileSize, and logging.maxFiles in config.yaml. A relative directory (the default, logs) resolves under the app working directory (/app/logs in the container); an absolute path is honored as-is.

⁠Optional: Update Check

Enabled by default. Once a day, sends only the product name and running version.

Environment VariableYAML EquivalentDescriptionExample
UPDATE_CHECK_ENABLEDupdateCheck.enabledTurn the daily update check on or offtrue or false

Configuration Priority: Environment variables always override YAML:

Defaults → config.yaml → Environment Variables
⁠Complete Configuration Examples

Option 1: config.yaml Only (All Configuration)

# config.yaml - Complete example with all options

# Required: Database
database:
  host: mysql
  port: 3306
  username: root
  password: yourpassword
  name: usersconnect

# Required: Redis
redis:
  host: redis
  port: 6379
  password: yourredispassword  # Optional

# Required: JWT
jwt:
  accessTokenSecret: your-access-token-secret-here
  refreshTokenSecret: your-refresh-token-secret-here

# Optional: S3 Image Uploads (remove if not using)
s3:
  accessKey: AKIAIOSFODNN7EXAMPLE
  secretKey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
  bucketName: usersconnect-media
  region: us-east-1
  endpoint: https://s3.example.com  # Only for non-AWS S3

# Optional: SMTP Email (remove if not using)
smtp:
  host: smtp.zoho.com
  port: 465
  secure: true
  user: [email protected]
  password: your_smtp_password

# Optional: CORS (remove if not using)
cors:
  enabled: true
  allowedOrigins:
    - http://localhost:4200
    - https://app.example.com
  allowCredentials: true
  allowedMethods:
    - GET
    - POST
    - PUT
    - DELETE
  allowedHeaders:
    - Content-Type
    - Authorization

# Optional: Logging
logging:
  level: info  # debug, info, warn, error

# Optional: Update check (enabled by default)
updateCheck:
  enabled: true

Option 2: Environment Variables Only

environment:
  # Required
  - NODE_ENV=production
  - DATABASE_HOST=mysql
  - DATABASE_PORT=3306
  - DATABASE_USERNAME=root
  - DATABASE_PASSWORD=${DATABASE_PASSWORD}
  - DATABASE_NAME=usersconnect
  - REDIS_HOST=redis
  - REDIS_PORT=6379
  - REDIS_PASSWORD=${REDIS_PASSWORD}
  - ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
  - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
  # Optional: S3
  - S3_ACCESS_KEY=AKIA...
  - S3_SECRET_KEY=${S3_SECRET_KEY}
  - S3_BUCKET_NAME=usersconnect-media
  - S3_REGION=us-east-1
  - S3_ENDPOINT=https://s3.example.com
  # Optional: SMTP
  - SMTP_HOST=smtp.zoho.com
  - SMTP_PORT=465
  - SMTP_SECURE=true
  - [email protected]
  - SMTP_PASSWORD=${SMTP_PASSWORD}
  # Optional: CORS
  - CORS_ENABLED=true
  - CORS_ALLOWED_ORIGINS=http://localhost:4200
  - CORS_ALLOW_CREDENTIALS=true
  - CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE
  - CORS_ALLOWED_HEADERS=Content-Type,Authorization
  # Optional: Logging
  - LOG_LEVEL=info
  # Optional: Update check (enabled by default)
  - UPDATE_CHECK_ENABLED=true

Option 3: Mixed (Secrets in ENV, Config in YAML)

# docker-compose.yml
volumes:
  - ./config.yaml:/app/config.yaml
environment:
  # Secrets only
  - DATABASE_PASSWORD=${DATABASE_PASSWORD}
  - REDIS_PASSWORD=${REDIS_PASSWORD}
  - ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
  - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
  - S3_SECRET_KEY=${S3_SECRET_KEY}
  - SMTP_PASSWORD=${SMTP_PASSWORD}
# config.yaml
database:
  host: mysql
  port: 3306
  username: root
  name: usersconnect

redis:
  host: redis
  port: 6379

s3:
  accessKey: AKIA...
  bucketName: usersconnect-media
  region: us-east-1
  endpoint: https://s3.example.com

smtp:
  host: smtp.zoho.com
  port: 465
  secure: true
  user: [email protected]

cors:
  enabled: true
  allowedOrigins:
    - http://localhost:4200
    - https://app.example.com
  allowCredentials: true
  allowedMethods:
    - GET
    - POST
    - PUT
    - DELETE
  allowedHeaders:
    - Content-Type
    - Authorization

logging:
  level: info

ā šŸ–¼ļø S3 Image Uploads (Optional)

⁠Supported Providers

Works with any S3-compatible storage:

  • āœ… AWS S3 - Industry standard
  • āœ… DigitalOcean Spaces - Simple pricing ($5/month)
  • āœ… Cloudflare R2 - No egress fees
  • āœ… Backblaze B2 - Cost-effective
⁠How It Works
  • With S3 configured: Image upload enabled for posts and profile pictures
  • Without S3: Upload fields hidden, all other features work normally, Gravatar used for avatars
⁠Self-Hosted S3 Storage

āš ļø The MinIO example has been removed. MinIO withdrew its community edition images from both Docker Hub and quay.io in September 2026, so they can no longer be pulled. A tested, maintained self-hosted alternative will be documented in an upcoming release. Until then, use any S3-compatible provider from the list above. Existing MinIO setups keep running from Docker's local cache, so avoid re-pulling the MinIO image.


ā šŸ“§ Email Features (Optional)

When SMTP is configured:

  • āœ… Email Verification - 6-digit code verification for new accounts
  • āœ… Password Reset - Secure password recovery via email
  • āœ… Email Change - Verify both old and new email addresses
  • āœ… Email Privacy - Users can show/hide email on profile

When SMTP is not configured:

  • āœ… Users can register and use the platform immediately
  • āœ… All social features work normally
  • āŒ Email verification not available
  • āŒ Password reset not available

ā šŸŽÆ Core Features

  • šŸ” JWT Authentication - Secure access/refresh token pattern
  • šŸ“ Posts - Create, edit, delete with optional images
  • šŸ’¬ Comments - Nested threaded discussions
  • šŸ‘šŸ‘Ž Reactions - Like and dislike posts/comments (separate counters)
  • šŸ‘¤ User Profiles - Gravatar or custom profile pictures
  • šŸ”„ Paginated Feed - Redis-cached for performance
  • šŸ›”ļø Admin Dashboard - User management and moderation
  • šŸ”” Update Notifications - Admins are told when a new release is available
  • šŸŽØ Responsive Design - Mobile-first Bootstrap 5 interface

ā šŸ”’ Security Best Practices

  • āœ… HTTPS Required: Auth cookies are Secure/SameSite=None, so the app must be served over HTTPS via a reverse proxy (Nginx/Caddy/Traefik/Nginx Proxy Manager)
  • āœ… CSRF Protection: State-changing requests from other websites are rejected. If you run a separate frontend on another origin, list it in cors.allowedOrigins
  • āœ… Strong Secrets: Generate with openssl rand -base64 32
  • āœ… Environment Variables: Never hardcode secrets
  • āœ… Regular Updates: Keep Docker images and dependencies updated
  • āœ… Minimal Permissions: Use least-privilege for S3 IAM users
  • āœ… Key Rotation: Rotate S3 keys every 90 days

ā šŸ“Š System Requirements

Minimum:

  • MySQL 8.0+
  • Redis 7.0+
  • 1GB RAM
  • 10GB disk space
  • HTTPS reverse proxy (required for authentication)
  • amd64 or arm64 host (arm64 images from v1.2.0)

Recommended:

  • MySQL 8.0+
  • Redis 7.0+
  • 2GB RAM
  • 20GB disk space (for media storage)
  • S3-compatible storage for images
  • SMTP server for email features

The Docker image bundles its own Node.js runtime; a local Node.js install is only needed when building from source.


ā šŸ› Troubleshooting

⁠Login Not Working / Cookies Rejected
  • Ensure the app is served over HTTPS — auth cookies are Secure and browsers reject them over plain HTTP
  • Confirm your reverse proxy terminates TLS and forwards to the container
  • For local development, use HTTPS (http://localhost is treated as secure by Chrome/Firefox; LAN-IP access from a phone needs real TLS)
  • If using a separate frontend, ensure CORS allowCredentials: true and your origin is in allowedOrigins
⁠Separate Frontend Gets 403 "cross-site request blocked"
  • v1.2.0 rejects state-changing requests whose origin isn't trusted
  • Add your frontend's exact origin (scheme, host and port) to cors.allowedOrigins
  • The app's own pages are unaffected
⁠Database Connection Failed
  • Ensure MySQL is running and accessible
  • Check DATABASE_HOST matches your setup
  • Wait for MySQL to fully initialize (~30 seconds)
  • Add health checks to docker-compose
⁠Images Not Uploading
  • Verify all S3 environment variables are set correctly
  • Check S3 bucket exists and is accessible
  • Verify IAM permissions (PutObject, GetObject, DeleteObject)
  • Check bucket policy allows public GetObject
⁠Redis Connection Failed
  • Ensure Redis is running
  • Check REDIS_HOST is correct
  • Verify REDIS_PASSWORD if set
⁠Setup Wizard Not Appearing
  • Setup wizard only appears if no admin exists
  • Check database is empty or create fresh database
  • Clear browser cache and try again
⁠Email Features Not Working
  • Verify all SMTP environment variables are set
  • Test SMTP credentials with email client
  • Check SMTP port (465 for SSL, 587 for TLS)
  • Verify firewall allows outbound SMTP connections
⁠"Update check failed" in the Logs
  • The server couldn't reach the check-in service. The app is unaffected and retries the next day
  • Common behind firewalls that block outbound HTTPS
  • To stop the check entirely, set UPDATE_CHECK_ENABLED=false

ā šŸ“š Documentation


ā šŸ·ļø Available Tags

  • latest - Latest stable release
  • 1.2.0 - Current release (amd64 and arm64)
  • 1.1.0, 1.0.4, 1.0.3, 1.0.2, 1.0.1, 1.0.0 - Previous releases (amd64 only)

Pull a specific version:

docker pull omairsalman/usersconnect:1.2.0

ā šŸ”„ Upgrading

# Backup database first
docker exec mysql mysqldump -u root -p usersconnect > backup.sql

# Pull the new image
docker pull omairsalman/usersconnect:latest

# Recreate containers (migrations run automatically)
docker-compose down
docker-compose up -d

# Verify in logs
docker-compose logs app

No database changes in v1.2.0. HTTPS is still required for authentication (since v1.1.0). If you use a separate frontend on another origin, make sure it's listed in cors.allowedOrigins; see Troubleshooting.


ā šŸ’» Building from Source

git clone https://github.com/omairsalman03/UsersConnect.git
cd UsersConnect
git checkout v1.2.0
npm install
npm run build
docker build -t usersconnect:custom .

ā šŸ“„ License

TagsLicense
1.2.0 and laterAGPL-3.0-only⁠
1.1.0 and earlier, including 1.0.x, the 1.0.0-rc tags, v0.1 and v0.2MIT⁠

latest is licensed according to the version it points to. See LICENSING.md⁠.


ā šŸ‘Øā€šŸ’» Author

Omair Salman

Initially developed during summer field training at AsalTech⁠, with continued development thereafter.


⁠🌟 Support

⭐ Star on GitHub: https://github.com/omairsalman03/UsersConnect⁠
šŸ’¬ Discussions: https://github.com/omairsalman03/UsersConnect/discussions⁠
šŸ› Report Issues: https://github.com/omairsalman03/UsersConnect/issues⁠


Latest Release: v1.2.0 šŸŽ‰

Tag summary

Content type

Image

Digest

sha256:52b349f58…

Size

113.8 MB

Last updated

about 1 month ago

docker pull omairsalman/usersconnect