Skip to content

Latest commit

 

History

History
480 lines (349 loc) · 10.3 KB

File metadata and controls

480 lines (349 loc) · 10.3 KB

Loops Installation Guide

Loops is a TikTok-like video sharing platform (with ActivityPub federation) built with Laravel. This guide covers installation, configuration, and deployment.

System Requirements

Minimum Versions

  • PHP: 8.3+
  • MySQL: 8.0+
  • Redis: 6.0+
  • FFmpeg: 4.5+ (8.0+ recommended)
  • Node.js: 20+
  • Composer: 2.0+

PHP Extensions Required

  • BCMath
  • Ctype
  • Fileinfo
  • JSON
  • Mbstring
  • OpenSSL
  • PDO
  • Tokenizer
  • XML
  • GD or Imagick
  • Redis

Installation

1. Clone the Repository

git clone https://github.com/joinloops/loops-server.git
cd loops-server

2. Install PHP Dependencies

composer install --no-dev --optimize-autoloader

3. Install Node Dependencies

npm install
npm run build

4. Environment Configuration

Copy the environment file and configure:

cp .env.example .env

Generate application key:

php artisan key:generate

Link storage directory:

php artisan storage:link

5. Configure Environment Variables

Edit .env file with your settings:

# Application
APP_NAME="Loops"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://your-domain.com

# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=loops
DB_USERNAME=loops_user
DB_PASSWORD=your_secure_password

# Redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_DB=0

# Cache & Sessions
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis

# Mail Configuration (choose one)
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

# Storage
# Loops uses S3-compatible storage by default. To use S3, uncomment and fill
# in the values below. To run on local disk instead, leave them commented and
# see "Using Local Storage Instead of S3" below.
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
# AWS_DEFAULT_REGION=us-east-1
# AWS_BUCKET=
# AWS_USE_PATH_STYLE_ENDPOINT=false
# AWS_URL=

# Video Processing
FFMPEG_BINARIES=/usr/bin/ffmpeg
FFPROBE_BINARIES=/usr/bin/ffprobe

Using Local Storage Instead of S3

Note

By default Loops stores avatars and videos on S3-compatible storage. If you'd rather keep everything on your server's local disk, the steps below switch storage over with a single config change — no code modifications required.

Loops resolves its media disk by the name s3. Avatars, videos, and thumbnails are all read and written through Storage::disk('s3') and the FFmpeg processing pipeline. Because Laravel disks are just named configuration entries, you can repoint that name at the local driver and every storage call will use your server's filesystem instead of an S3 bucket.

1. Redefine the s3 Disk

Open config/filesystems.php and replace the s3 disk definition with a local one:

's3' => [
    'driver' => 'local',
    'root' => storage_path('app/public'),
    'url' => env('APP_URL').'/storage',
    'visibility' => 'public',
    'throw' => false,
],

The disk keeps the name s3, but now stores files under storage/app/public and serves them from APP_URL/storage. Any AWS_* values in your .env are simply ignored.

2. Confirm the Storage Symlink

You already ran this in step 4 of installation, but if not:

php artisan storage:link

This symlinks public/storage to storage/app/public so uploaded media is reachable over HTTP.

3. Rebuild Cached Config

If you've cached your configuration (as recommended for production), rebuild it so the change takes effect:

php artisan config:clear
php artisan config:cache

Caveats

  • Disk space: All avatars and videos now live on your server's disk. Make sure the volume backing storage/ has room for your expected media, and keep an eye on it as your instance grows.
  • The disk is still named s3: This is cosmetic. The name is only an identifier, so everything works — it just looks unusual in your config.
  • Serving: Files are served directly by your web server from APP_URL/storage rather than streamed through PHP. The Nginx and Apache examples in this guide already expose the storage symlink.
  • Permissions: Ensure storage/ is writable by your web and queue user (see File Permissions).
  • Custom URLs: This relies on media URLs being generated through Storage::disk('s3')->url(). If you've customized anything to build URLs directly from AWS_URL, those spots won't pick up the local path.

This approach works reliably today. A dedicated media-disk environment variable is planned to make the choice between local and S3 storage more explicit down the line.

Database Setup

1. Create Database

CREATE DATABASE loops CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'loops_user'@'localhost' IDENTIFIED BY 'your_secure_password';
GRANT ALL PRIVILEGES ON loops.* TO 'loops_user'@'localhost';
FLUSH PRIVILEGES;

2. Run Migrations

php artisan migrate

3. Create Admin Account

Since registration is disabled by default, create your first admin account:

php artisan create-admin-account

Follow the prompts to set up your admin credentials.

4. Generate Passport Keys

php artisan passport:keys

Queue Configuration

Loops uses Redis-backed queues with Horizon for queue management.

1. Publish Horizon Configuration

php artisan vendor:publish --provider="Laravel\Horizon\HorizonServiceProvider"

2. Configure Horizon

Edit config/horizon.php as needed for your environment.

3. Start Horizon

php artisan horizon

For production, use a process manager like Supervisor (make sure you replace the paths and user accordingly):

[program:loops-horizon]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/loops/artisan horizon
autostart=true
autorestart=true
user=www
redirect_stderr=true
stdout_logfile=/var/www/loops/storage/logs/horizon.log
stopwaitsecs=3600

Mail Configuration

Loops supports multiple mail providers. Configure one of the following:

SMTP

MAIL_MAILER=smtp
MAIL_HOST=your-smtp-host
MAIL_PORT=587
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls

Mailgun

MAIL_MAILER=mailgun
MAILGUN_DOMAIN=your-domain.com
MAILGUN_SECRET=your-secret-key

Amazon SES

MAIL_MAILER=ses
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_DEFAULT_REGION=us-east-1

Postmark

MAIL_MAILER=postmark
POSTMARK_TOKEN=your-server-token

Resend

MAIL_MAILER=resend
RESEND_KEY=your-api-key

Optional Features

Captcha Protection

Loops supports Cloudflare Turnstile and hCaptcha for spam protection.

Cloudflare Turnstile

LOOPS_CAPTCHA=true
LOOPS_CAPTCHA_DRIVER=turnstile
TURNSTILE_SITE_KEY=your-site-key
TURNSTILE_SECRET_KEY=your-secret-key

hCaptcha

LOOPS_CAPTCHA=true
LOOPS_CAPTCHA_DRIVER=hcaptcha
HCAPTCHA_SITE_KEY=your-site-key
HCAPTCHA_SECRET_KEY=your-secret-key

Two-Factor Authentication

2FA is supported. No additional configuration required - users can enable it in their profile settings.

Web Server Configuration

Nginx Configuration

server {
    listen 80;
    listen [::]:80;
    server_name your-domain.com;
    root /var/www/loops/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

    # Handle large video uploads
    client_max_body_size 100M;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_hide_header X-Powered-By;
    }
}

Apache Configuration

<VirtualHost *:80>
    ServerName your-domain.com
    DocumentRoot /var/www/loops/public

    <Directory /var/www/loops/public>
        AllowOverride All
        Require all granted
    </Directory>

    # Handle large video uploads
    LimitRequestBody 104857600

    ErrorLog ${APACHE_LOG_DIR}/loops_error.log
    CustomLog ${APACHE_LOG_DIR}/loops_access.log combined
</VirtualHost>

File Permissions

Set appropriate permissions:

sudo chown -R www-data:www-data /var/www/loops
sudo chmod -R 755 /var/www/loops
sudo chmod -R 775 /var/www/loops/storage
sudo chmod -R 775 /var/www/loops/bootstrap/cache

Cron Jobs

Add to your crontab:

# Laravel Scheduler
* * * * * cd /var/www/loops && php artisan schedule:run >> /dev/null 2>&1

Production Optimization

1. Optimize Configuration

php artisan config:cache
php artisan route:cache
php artisan view:cache

2. Enable OPcache

Add to your PHP configuration:

opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=2
opcache.fast_shutdown=1

3. Configure PHP-FPM

Optimize php-fpm settings for video processing:

pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 1000

# Increase limits for video uploads
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 300
memory_limit = 512M

Security Considerations

  1. Disable debug mode in production (APP_DEBUG=false)
  2. Use HTTPS for all connections
  3. Regularly update dependencies
  4. Monitor logs for suspicious activity
  5. Use strong passwords and enable 2FA
  6. Keep FFmpeg updated for security patches

Updates

To update Loops:

# Pull latest changes
git pull origin main

# Update dependencies
composer install --no-dev --optimize-autoloader

# Install frontend deps + rebuild frontend
npm ci && npm run build

# Run migrations
php artisan migrate

# Clear caches
php artisan cache:clear
php artisan config:clear
php artisan view:clear

# Rebuild caches
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Restart services
php artisan horizon:terminate