File Sharing beginner ⏱ 35 minutes

Pingvin Share: Self-Hosted File Sharing Platform

Deploy Pingvin Share for secure, temporary file sharing with expiring links, password protection, and a beautiful interface. The privacy-focused alternative to WeTransfer.

By Homelab Team • 2025-01-15
#pingvin-share#file-sharing#temporary-files#expiring-links#wetransfer-alternative#docker#homelab

Overview

Pingvin Share is a beautiful, self-hosted file sharing solution that makes it incredibly easy to share files with anyone, even people outside your network. Think of it as your personal WeTransfer or Firefox Send - but completely under your control, with no file size limits, no subscription fees, and complete privacy.

Built with modern web technologies (Next.js and NestJS), Pingvin Share offers an elegant, responsive interface that works flawlessly on any device. Whether you need to send a large video file to a client, share project assets with your team, or provide temporary download links to family members, Pingvin Share handles it all with style and security.

What sets Pingvin Share apart is its focus on temporary, secure file sharing. Files automatically expire after a set time, links can be password-protected, and you control exactly how many times a file can be downloaded. It’s perfect for sensitive documents, one-time transfers, or any scenario where you don’t want files floating around forever.

What You’ll Learn:

  • Deploy Pingvin Share using Docker Compose with PostgreSQL
  • Configure file upload limits and storage quotas
  • Create expiring share links with custom lifetimes
  • Implement password protection and download limits
  • Set up email notifications for share activity
  • Configure reverse proxy for secure remote access
  • Customize branding and appearance
  • Implement automated cleanup of expired files
  • Monitor storage usage and share statistics

Why Choose Pingvin Share?

Unlike permanent file storage solutions like FileBrowser or Nextcloud, Pingvin Share is purpose-built for temporary sharing. It’s the perfect complement to your file management stack - use it when you need to quickly send files to someone without creating accounts, managing permissions, or worrying about cleanup. Files automatically disappear when they expire, keeping your storage clean and your data secure.


Prerequisites

Before starting, ensure you have:

Required:

  • Docker and Docker Compose installed
  • Basic understanding of Docker networking
  • 512 MB RAM minimum (1 GB recommended)
  • Network ports 3000 available (or your chosen port)
  • Storage space for uploaded files (plan for 10-50 GB minimum)

Optional but Recommended:

  • Reverse proxy (Traefik, Nginx Proxy Manager, Caddy)
  • Domain name for public sharing
  • SMTP server for email notifications
  • PostgreSQL knowledge (basic)

Pre-Deployment Checklist:

  • Docker environment is functional
  • PostgreSQL or Docker PostgreSQL ready
  • Port 3000 is available (or alternative chosen)
  • Storage location with adequate space identified
  • Domain name configured (if using HTTPS)
  • SMTP credentials prepared (if using email)

Understanding Pingvin Share

Core Features

Share Management:

  • Upload single files or multiple files per share
  • Set expiration time (hours, days, weeks)
  • Limit number of downloads per share
  • Password protection for sensitive files
  • Custom share descriptions and messages
  • One-click link copying and QR codes

Security:

  • Automatic file deletion after expiration
  • Optional password protection
  • Download limit enforcement
  • No account required for recipients
  • Configurable maximum file sizes
  • Rate limiting to prevent abuse

User Experience:

  • Clean, modern web interface
  • Mobile-responsive design
  • Drag-and-drop file uploads
  • Upload progress indicators
  • Direct download links (no forced landing pages)
  • QR code generation for mobile sharing

Administration:

  • Admin dashboard for monitoring
  • User management (optional)
  • Storage quota configuration
  • System statistics and analytics
  • Configurable retention policies
  • Manual share cleanup

Architecture Overview

User Browser
     ↓
Nginx/Reverse Proxy (HTTPS)
     ↓
Pingvin Share Frontend (Next.js) - Port 3000
     ↓
Pingvin Share Backend (NestJS)
     ↓
PostgreSQL Database
     ↓
File Storage (Docker Volume or Host Mount)

Components:

  1. Frontend: Next.js application serving the web UI
  2. Backend: NestJS API handling uploads, shares, and logic
  3. Database: PostgreSQL storing share metadata, settings
  4. Storage: File system or S3-compatible storage for uploaded files

How It Works

Creating a share:

  1. User uploads files via web interface
  2. Files stored on server with random IDs
  3. Share link generated with unique token
  4. Metadata saved to database (expiration, password, limits)
  5. Link shared with recipients

Accessing a share:

  1. Recipient clicks share link
  2. Pingvin Share validates token and checks expiration
  3. Password prompt shown (if protected)
  4. Download counter incremented
  5. Files served directly or via download page

Expiration and cleanup:

  1. Background job runs periodically
  2. Checks for expired shares
  3. Deletes associated files from storage
  4. Removes database records
  5. Storage reclaimed automatically

Installation and Deployment

The complete, production-ready deployment.

Create directory structure:

# Create Pingvin Share directory
mkdir -p /opt/pingvin-share/{data,database}
cd /opt/pingvin-share

Create docker-compose.yml:

version: '3.8'

services:
  pingvin-share:
    image: stonith404/pingvin-share:latest
    container_name: pingvin-share
    restart: unless-stopped

    # Environment variables
    environment:
      # Database connection
      - DATABASE_URL=postgresql://pingvin:secure_db_password@postgres:5432/pingvin

      # Backend configuration
      - BACKEND_URL=http://localhost:3000
      - FRONTEND_URL=http://localhost:3000

      # File upload limits (in bytes)
      # 10 GB max file size
      - MAX_FILE_SIZE=10737418240

      # Share defaults
      - DEFAULT_EXPIRATION=7d
      - MAX_EXPIRATION=30d

      # Email configuration (optional)
      - SMTP_HOST=smtp.gmail.com
      - SMTP_PORT=587
      - SMTP_USER=your-[email protected]
      - SMTP_PASSWORD=your-app-password
      - SMTP_FROM=Pingvin Share <[email protected]>

      # Security
      - ALLOW_REGISTRATION=false
      - ALLOW_UNAUTHENTICATED_SHARES=true

    # Port mapping
    ports:
      - "3000:3000"

    # Volume mounts
    volumes:
      # Upload storage
      - ./data:/opt/app/backend/data
      # Optional: Config persistence
      - ./config:/opt/app/backend/config

    # Dependencies
    depends_on:
      postgres:
        condition: service_healthy

    # Security options
    security_opt:
      - no-new-privileges:true

    # Resource limits
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M

    networks:
      - pingvin-net

  postgres:
    image: postgres:16-alpine
    container_name: pingvin-postgres
    restart: unless-stopped

    environment:
      - POSTGRES_DB=pingvin
      - POSTGRES_USER=pingvin
      - POSTGRES_PASSWORD=secure_db_password

    volumes:
      - ./database:/var/lib/postgresql/data

    # Health check
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U pingvin"]
      interval: 10s
      timeout: 5s
      retries: 5

    networks:
      - pingvin-net

networks:
  pingvin-net:
    driver: bridge

Important: Change secure_db_password to a strong, unique password!

Deploy Pingvin Share:

# Start all services
docker-compose up -d

# View logs
docker-compose logs -f pingvin-share

# Check status
docker-compose ps

# Verify database connection
docker-compose logs postgres | grep "ready to accept connections"

Access Pingvin Share:

Open your browser to http://YOUR-SERVER-IP:3000

Method 2: Docker Compose with SQLite (Simpler)

For testing or low-traffic deployments:

version: '3.8'

services:
  pingvin-share:
    image: stonith404/pingvin-share:latest
    container_name: pingvin-share
    restart: unless-stopped

    environment:
      # SQLite database (file-based)
      - DATABASE_URL=file:./data/pingvin.db
      - BACKEND_URL=http://localhost:3000
      - FRONTEND_URL=http://localhost:3000
      - MAX_FILE_SIZE=5368709120  # 5 GB

    ports:
      - "3000:3000"

    volumes:
      - ./data:/opt/app/backend/data

    networks:
      - pingvin-net

networks:
  pingvin-net:
    driver: bridge

Deploy:

docker-compose up -d

Note: SQLite is simpler but less performant for high traffic.

Initial Setup

Create admin account:

  1. Open http://YOUR-SERVER-IP:3000
  2. Click Sign Up (if registration enabled)
  3. Create your admin account
  4. Log in

If registration is disabled:

# Create admin user via CLI
docker exec -it pingvin-share npm run user:create

# Follow prompts to create admin user

Configuration

Environment Variables

Core settings:

# Database
DATABASE_URL: PostgreSQL or SQLite connection string

# URLs (important for reverse proxy)
BACKEND_URL: Backend API URL (internal or external)
FRONTEND_URL: Frontend URL (what users see)

# File limits
MAX_FILE_SIZE: Maximum file size in bytes (default: 1 GB)

# Share defaults
DEFAULT_EXPIRATION: Default expiration time (e.g., "7d", "24h")
MAX_EXPIRATION: Maximum allowed expiration (e.g., "30d")

File size examples:

# 500 MB
MAX_FILE_SIZE=524288000

# 1 GB
MAX_FILE_SIZE=1073741824

# 5 GB
MAX_FILE_SIZE=5368709120

# 10 GB
MAX_FILE_SIZE=10737418240

# 50 GB (requires lots of RAM and disk space)
MAX_FILE_SIZE=53687091200

Expiration format:

# Hours
DEFAULT_EXPIRATION=24h
DEFAULT_EXPIRATION=72h

# Days
DEFAULT_EXPIRATION=7d
DEFAULT_EXPIRATION=30d

# Weeks
DEFAULT_EXPIRATION=2w
DEFAULT_EXPIRATION=4w

User Registration Settings

Disable public registration:

ALLOW_REGISTRATION=false

Users must be created by admin.

Enable public registration:

ALLOW_REGISTRATION=true

Anyone can create an account.

Anonymous shares:

# Allow unauthenticated users to create shares
ALLOW_UNAUTHENTICATED_SHARES=true

# Require login to create shares
ALLOW_UNAUTHENTICATED_SHARES=false

Email Configuration

Gmail example:

SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-[email protected]
SMTP_PASSWORD=your-app-password
SMTP_FROM=Pingvin Share <[email protected]>

Custom SMTP server:

SMTP_HOST=mail.yourdomain.com
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=smtp_password
SMTP_FROM=File Sharing <[email protected]>
SMTP_SECURE=true  # Use TLS

Email notifications include:

  • Share creation confirmation
  • Share download notifications (optional)
  • Share expiration warnings

Creating and Managing Shares

Creating a Share

Via Web Interface:

  1. Log in (if required)
  2. Click Create Share or New Share
  3. Upload files:
    • Drag and drop into upload area
    • Or click to browse files
    • Multiple files supported
  4. Configure share settings:
    • Expiration: When share expires
    • Max Downloads: Limit number of downloads (optional)
    • Password: Protect with password (optional)
    • Description: Add message for recipients
  5. Click Create Share
  6. Copy share link or QR code

Share link format:

https://share.yourdomain.com/s/UNIQUE-TOKEN-HERE

Share Settings Explained

Expiration:

  • Never: Share never expires (not recommended)
  • 1 hour: For very temporary shares
  • 6 hours: Same-day sharing
  • 24 hours: One-day access
  • 7 days: One-week access (default)
  • 30 days: Long-term sharing
  • Custom: Set specific date/time

Max Downloads:

  • Unlimited: Anyone can download any number of times
  • 1 download: One-time access (link becomes invalid after)
  • 5 downloads: Limited sharing
  • Custom: Set specific number

Password Protection:

  • None: No password required
  • Custom password: Recipients must enter password

Best practice combinations:

Sensitive documents:

Expiration: 24 hours
Max Downloads: 1
Password: Yes

Team file sharing:

Expiration: 7 days
Max Downloads: 10
Password: Yes (shared with team)

Public downloads:

Expiration: 30 days
Max Downloads: Unlimited
Password: No

One-time transfer:

Expiration: 1 hour
Max Downloads: 1
Password: Optional

Managing Your Shares

View active shares:

  1. Log in to Pingvin Share
  2. Go to My Shares or Dashboard
  3. See all your active shares

For each share, you can:

  • View share details (views, downloads, expiration)
  • Copy share link
  • Download QR code
  • Delete share early
  • Extend expiration (if allowed)
  • Change password

Delete a share:

  1. Find share in My Shares
  2. Click Delete or trash icon
  3. Confirm deletion
  4. Files immediately deleted from server

Copy link:

Click the copy icon next to the share link.

QR Code:

Click the QR code icon to generate a scannable code:

  • Perfect for mobile transfers
  • Share via screenshot or print
  • No typing required

Email link:

Use the email button to send share link via configured SMTP.


Reverse Proxy Configuration

Why HTTPS is Essential

For Pingvin Share to work properly over the internet:

  1. Security: Encrypt file transfers
  2. Browser compatibility: Modern browsers require HTTPS for many features
  3. Professional appearance: No security warnings
  4. Trust: Recipients more likely to download from HTTPS sites

Traefik Configuration

Update docker-compose.yml:

version: '3.8'

services:
  pingvin-share:
    image: stonith404/pingvin-share:latest
    container_name: pingvin-share
    restart: unless-stopped

    environment:
      - DATABASE_URL=postgresql://pingvin:secure_db_password@postgres:5432/pingvin
      # Important: Set to your public URL
      - BACKEND_URL=https://share.yourdomain.com
      - FRONTEND_URL=https://share.yourdomain.com
      - MAX_FILE_SIZE=10737418240

    volumes:
      - ./data:/opt/app/backend/data

    depends_on:
      - postgres

    networks:
      - traefik
      - pingvin-net

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.pingvin.rule=Host(`share.yourdomain.com`)"
      - "traefik.http.routers.pingvin.entrypoints=websecure"
      - "traefik.http.routers.pingvin.tls.certresolver=letsencrypt"
      - "traefik.http.services.pingvin.loadbalancer.server.port=3000"
      # Increase upload size limit
      - "traefik.http.middlewares.pingvin-buffering.buffering.maxRequestBodyBytes=10737418240"
      - "traefik.http.routers.pingvin.middlewares=pingvin-buffering"

  postgres:
    # ... (same as before)
    networks:
      - pingvin-net

networks:
  traefik:
    external: true
  pingvin-net:
    driver: bridge

Deploy:

docker-compose up -d

Access at: https://share.yourdomain.com

Nginx Proxy Manager

  1. Log into Nginx Proxy Manager
  2. Add Proxy Host:
    • Domain Names: share.yourdomain.com
    • Scheme: http
    • Forward Hostname/IP: pingvin-share (or container IP)
    • Forward Port: 3000
  3. Custom locations (for upload limits):
    • Location: /
    • Advanced config:
      client_max_body_size 10G;
      proxy_request_buffering off;
  4. SSL tab: Request Let’s Encrypt certificate
  5. Enable Force SSL

Caddy Configuration

Caddyfile:

share.yourdomain.com {
    reverse_proxy pingvin-share:3000

    # Increase upload size limit
    request_body {
        max_size 10GB
    }

    # Increase timeout for large uploads
    timeouts {
        read_body 1h
        write 1h
    }
}

Nginx Standalone

nginx.conf:

server {
    listen 443 ssl http2;
    server_name share.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/share.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/share.yourdomain.com/privkey.pem;

    # Large file upload support
    client_max_body_size 10G;
    client_body_timeout 300s;

    location / {
        proxy_pass http://pingvin-share:3000;
        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;

        # Upload streaming
        proxy_request_buffering off;
        proxy_buffering off;

        # Timeouts for large uploads
        proxy_connect_timeout 300s;
        proxy_send_timeout 300s;
        proxy_read_timeout 300s;
    }
}

Storage Management

File Storage Location

Default location:

volumes:
  - ./data:/opt/app/backend/data

Files stored in: /opt/pingvin-share/data/uploads/

Custom storage location:

volumes:
  - /mnt/large-storage/pingvin-uploads:/opt/app/backend/data

Ensure proper permissions:

# Set ownership (UID 1000 in container)
sudo chown -R 1000:1000 /opt/pingvin-share/data

# Set permissions
sudo chmod -R 755 /opt/pingvin-share/data

Storage Quotas

Global storage limit:

Set via environment variable:

environment:
  - MAX_TOTAL_STORAGE=107374182400  # 100 GB total

Per-user quotas:

Configure in admin dashboard after login.

Monitoring Storage Usage

Check disk usage:

# Check data directory size
du -sh /opt/pingvin-share/data

# Detailed breakdown
du -h --max-depth=1 /opt/pingvin-share/data

# Check container disk usage
docker system df -v

Automated monitoring script:

#!/bin/bash
# /opt/scripts/pingvin-storage-check.sh

DATA_DIR="/opt/pingvin-share/data"
THRESHOLD=80  # Alert at 80% usage

USAGE=$(df -h "$DATA_DIR" | tail -1 | awk '{print $5}' | sed 's/%//')

if [ "$USAGE" -gt "$THRESHOLD" ]; then
    echo "WARNING: Pingvin Share storage at ${USAGE}%"
    # Add notification (email, webhook, etc.)
fi

echo "Storage usage: ${USAGE}%"

Cleanup Policies

Automatic expiration:

Pingvin Share automatically deletes expired shares based on configured expiration times.

Manual cleanup:

# Stop Pingvin Share
docker-compose stop pingvin-share

# Remove old files (example: older than 30 days)
find /opt/pingvin-share/data/uploads -type f -mtime +30 -delete

# Remove empty directories
find /opt/pingvin-share/data/uploads -type d -empty -delete

# Restart
docker-compose start pingvin-share

Cleanup script with database sync:

#!/bin/bash
# /opt/scripts/pingvin-cleanup.sh

# Trigger internal cleanup via API (if available)
# Or use database queries to identify orphaned files

docker exec pingvin-share node /opt/app/backend/dist/cleanup.js

echo "Cleanup completed: $(date)"

Security Hardening

Password Strength

Enforce strong passwords for shares:

Educate users to use:

  • At least 12 characters
  • Mix of letters, numbers, symbols
  • Generated passwords (password manager)

Example secure passwords:

xK9#mP2$vL7@qR4!
TigerElephant$92Cloud
2025-Secure-Share-Pass!

Rate Limiting

Prevent abuse with rate limiting:

Nginx rate limiting:

limit_req_zone $binary_remote_addr zone=upload:10m rate=5r/m;

location /api/upload {
    limit_req zone=upload burst=10 nodelay;
    proxy_pass http://pingvin-share:3000;
}

Cloudflare rate limiting:

Use Cloudflare’s Rate Limiting rules to protect upload endpoints.

File Type Restrictions

Block dangerous file types (optional):

Configure via reverse proxy:

location ~ .(exe|bat|cmd|sh|dll|msi)$ {
    return 403 "File type not allowed";
}

Better approach: Scan with ClamAV or similar:

services:
  clamav:
    image: clamav/clamav:latest
    container_name: clamav
    volumes:
      - ./clamav:/var/lib/clamav
    networks:
      - pingvin-net

Integrate ClamAV scanning with upload pipeline (requires custom code).

HTTPS Only

Force HTTPS:

server {
    listen 80;
    server_name share.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

Strict Transport Security (HSTS):

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Database Security

Secure PostgreSQL:

postgres:
  environment:
    # Strong password
    - POSTGRES_PASSWORD=VERY-LONG-RANDOM-PASSWORD-HERE

    # Disable remote connections (use Docker network only)
  networks:
    - pingvin-net  # Internal only, no external network

Backup database regularly:

# Backup PostgreSQL
docker exec pingvin-postgres pg_dump -U pingvin pingvin > pingvin-backup-$(date +%Y%m%d).sql

Backup and Restore

What to Backup

  1. Database: All share metadata
  2. Uploaded files: /opt/pingvin-share/data
  3. Configuration: docker-compose.yml and .env

Automated Backup Script

#!/bin/bash
# /opt/scripts/backup-pingvin.sh

BACKUP_DIR="/mnt/backups/pingvin"
DATE=$(date +%Y%m%d_%H%M%S)
DATA_DIR="/opt/pingvin-share/data"

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Backup database
docker exec pingvin-postgres pg_dump -U pingvin pingvin | gzip > "$BACKUP_DIR/database-$DATE.sql.gz"

# Backup files
tar -czf "$BACKUP_DIR/files-$DATE.tar.gz" -C "$DATA_DIR" .

# Backup config
cp /opt/pingvin-share/docker-compose.yml "$BACKUP_DIR/docker-compose-$DATE.yml"

# Keep only last 14 days
find "$BACKUP_DIR" -name "database-*.sql.gz" -mtime +14 -delete
find "$BACKUP_DIR" -name "files-*.tar.gz" -mtime +14 -delete

echo "Backup completed: $DATE"

Automate with cron:

# Edit crontab
crontab -e

# Add daily backup at 2 AM
0 2 * * * /opt/scripts/backup-pingvin.sh >> /var/log/pingvin-backup.log 2>&1

Restore from Backup

# Stop Pingvin Share
docker-compose down

# Restore database
gunzip < /mnt/backups/pingvin/database-20250115_020000.sql.gz |   docker exec -i pingvin-postgres psql -U pingvin pingvin

# Restore files
tar -xzf /mnt/backups/pingvin/files-20250115_020000.tar.gz -C /opt/pingvin-share/data

# Set permissions
chown -R 1000:1000 /opt/pingvin-share/data

# Start Pingvin Share
docker-compose up -d

Troubleshooting

Issue 1: Upload Fails for Large Files

Symptoms:

  • Small files upload fine
  • Large files (100+ MB) fail or timeout

Solutions:

1. Increase reverse proxy limits:

See reverse proxy configuration section above.

2. Increase MAX_FILE_SIZE:

environment:
  - MAX_FILE_SIZE=10737418240  # 10 GB

3. Check disk space:

df -h /opt/pingvin-share/data

4. Increase timeouts:

proxy_read_timeout 600s;
proxy_send_timeout 600s;
client_body_timeout 600s;

Issue 2: Database Connection Failed

Symptoms:

  • Pingvin Share won’t start
  • Error: “Connection to database failed”

Diagnostics:

# Check PostgreSQL logs
docker logs pingvin-postgres

# Test database connection
docker exec pingvin-postgres psql -U pingvin -d pingvin -c "SELECT 1;"

Solutions:

1. Verify DATABASE_URL:

DATABASE_URL=postgresql://pingvin:PASSWORD@postgres:5432/pingvin

Ensure password matches PostgreSQL environment variable.

2. Wait for PostgreSQL:

Add healthcheck dependency:

depends_on:
  postgres:
    condition: service_healthy

3. Database doesn’t exist:

# Create database manually
docker exec -it pingvin-postgres psql -U pingvin -c "CREATE DATABASE pingvin;"

Issue 3: Shares Not Expiring

Symptoms:

  • Expired shares still accessible
  • Files not cleaned up

Solutions:

1. Check cleanup job:

Pingvin Share should run automatic cleanup. Check logs:

docker logs pingvin-share | grep cleanup

2. Manual cleanup:

# Trigger cleanup manually (if API exists)
docker exec pingvin-share npm run cleanup

# Or restart container
docker-compose restart pingvin-share

3. Database clock:

Ensure server time is correct:

date
timedatectl

Issue 4: Can’t Access Pingvin Share

Symptoms:

  • Connection refused
  • Page won’t load

Diagnostics:

# Check container status
docker ps | grep pingvin

# Check logs
docker logs pingvin-share

# Test locally
curl http://localhost:3000

Solutions:

Container not running:

docker-compose up -d

Port conflict:

# Change port in docker-compose.yml
ports:
  - "3001:3000"

Firewall blocking:

sudo ufw allow 3000/tcp

Issue 5: Email Notifications Not Working

Symptoms:

  • No emails sent
  • SMTP errors in logs

Diagnostics:

# Check logs for SMTP errors
docker logs pingvin-share | grep -i smtp
docker logs pingvin-share | grep -i email

Solutions:

1. Gmail: Use App Password

Don’t use regular password. Generate App Password:

  • Google Account → Security → 2-Step Verification → App Passwords

2. Test SMTP settings:

# Test SMTP from container
docker exec -it pingvin-share node -e "
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
  host: 'smtp.gmail.com',
  port: 587,
  auth: {
    user: '[email protected]',
    pass: 'your-app-password'
  }
});
transporter.sendMail({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Test',
  text: 'Test email'
}, console.log);
"

3. Check SMTP port:

Some ISPs block port 25. Use 587 (submission) or 465 (SSL).


Advanced Features

Custom Branding

Logo and colors:

Pingvin Share supports custom branding via admin panel:

  1. Log in as admin
  2. Go to Settings → Appearance
  3. Upload custom logo
  4. Set primary color
  5. Set accent color
  6. Save changes

API Usage

Pingvin Share provides REST API for automation:

Create share via API:

# Authenticate
TOKEN=$(curl -X POST http://localhost:3000/api/auth/login   -H "Content-Type: application/json"   -d '{"email":"[email protected]","password":"password"}' | jq -r '.token')

# Upload file
curl -X POST http://localhost:3000/api/shares   -H "Authorization: Bearer $TOKEN"   -F "file=@/path/to/file.pdf"   -F "expiration=7d"   -F "maxDownloads=5"

List shares:

curl -X GET http://localhost:3000/api/shares   -H "Authorization: Bearer $TOKEN"

S3-Compatible Storage

Use S3 or MinIO instead of local filesystem:

environment:
  - STORAGE_TYPE=s3
  - S3_ENDPOINT=https://s3.amazonaws.com
  - S3_BUCKET=pingvin-shares
  - S3_REGION=us-east-1
  - S3_ACCESS_KEY=YOUR-ACCESS-KEY
  - S3_SECRET_KEY=YOUR-SECRET-KEY

Benefits:

  • Unlimited scalable storage
  • Geographic redundancy
  • Cost-effective for large datasets

Integration with Other Services

Uptime monitoring:

Add HTTP monitor in Uptime Kuma pointing to Pingvin Share.

Notifications:

Use webhooks to notify other services when shares are created/downloaded.


Alternatives and Comparisons

Pingvin Share vs. WeTransfer

FeaturePingvin ShareWeTransfer
Self-Hosted✅ Yes❌ No
File Size LimitYour storage2 GB (free), 200 GB (paid)
CostFree (hosting cost only)Free/Paid subscription
PrivacyCompleteThird-party access
CustomizationFull controlLimited
ExpirationConfigurableFixed (7 days free, 30 days paid)

Pingvin Share vs. Firefox Send

Firefox Send (discontinued):

  • Mozilla project (no longer maintained)
  • End-to-end encryption
  • Simple interface

Pingvin Share (active):

  • Active development
  • More features (admin panel, user management)
  • PostgreSQL backend

Send alternative: Use Pingvin Share or Lufi for Firefox Send replacement.

Pingvin Share vs. FileBrowser

Different use cases:

FileBrowser: Permanent file storage and management Pingvin Share: Temporary file sharing with links

Use both together:

  • FileBrowser for internal file management
  • Pingvin Share for external file sharing

Conclusion

You now have a fully functional Pingvin Share deployment enabling secure, temporary file sharing.

Key Takeaways:

  1. Self-Hosted: Complete control over your file sharing
  2. Secure: Password protection, expiring links, download limits
  3. Simple: Easy to use for both senders and recipients
  4. Flexible: Configure expiration, limits, and notifications
  5. Clean: Automatic cleanup of expired files

Next Steps:

  • Set up HTTPS via reverse proxy for secure sharing
  • Configure email notifications for share activity
  • Set appropriate file size and expiration limits
  • Create backup automation for database and files
  • Share with your team or family

Related Guides:

  • FileBrowser: Web-Based File Manager
  • PairDrop: Local Network File Sharing
  • MinIO: S3-Compatible Object Storage

Welcome to secure, self-hosted file sharing!


Guide Version: 1.0 Last Updated: January 15, 2025 Tested With: Pingvin Share v0.20.x Estimated Read Time: 30-35 minutes Estimated Implementation Time: 35 minutes

Part of the homelabbing.io File Sharing & Transfer series