Loops is a TikTok-like video sharing platform (with ActivityPub federation) built with Laravel. This guide covers installation, configuration, and deployment.
Warning
S3-Compatible Storage Required Currently, Loops requires an S3-compatible filesystem (like AWS S3, MinIO, or DigitalOcean Spaces) for avatar and video storage. Support for local storage is on the way but is not yet implemented. Please ensure you have S3 credentials ready before proceeding.
- PHP: 8.3+
- MySQL: 8.0+
- Redis: 6.0+
- FFmpeg: 4.5+ (5.0+ recommended)
- Node.js: 18+
- Composer: 2.0+
- BCMath
- Ctype
- Fileinfo
- JSON
- Mbstring
- OpenSSL
- PDO
- Tokenizer
- XML
- GD or Imagick
- Redis
git clone https://github.com/joinloops/loops-server.git
cd loops-servercomposer install --no-dev --optimize-autoloadernpm install
npm run buildCopy the environment file and configure:
cp .env.example .envGenerate application key:
php artisan key:generateLink storage directory:
php artisan storage:linkEdit .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}"
# For S3 storage
# 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/ffprobeCREATE 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;php artisan migrateSince registration is disabled by default, create your first admin account:
php artisan create-admin-accountFollow the prompts to set up your admin credentials.
php artisan passport:keysLoops uses Redis-backed queues with Horizon for queue management.
php artisan vendor:publish --provider="Laravel\Horizon\HorizonServiceProvider"Edit config/horizon.php as needed for your environment.
php artisan horizonFor 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=3600Loops supports multiple mail providers. Configure one of the following:
MAIL_MAILER=smtp
MAIL_HOST=your-smtp-host
MAIL_PORT=587
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tlsMAIL_MAILER=mailgun
MAILGUN_DOMAIN=your-domain.com
MAILGUN_SECRET=your-secret-keyMAIL_MAILER=ses
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_DEFAULT_REGION=us-east-1MAIL_MAILER=postmark
POSTMARK_TOKEN=your-server-tokenMAIL_MAILER=resend
RESEND_KEY=your-api-keyLoops supports Cloudflare Turnstile and hCaptcha for spam protection.
LOOPS_CAPTCHA=true
LOOPS_CAPTCHA_DRIVER=turnstile
TURNSTILE_SITE_KEY=your-site-key
TURNSTILE_SECRET_KEY=your-secret-keyLOOPS_CAPTCHA=true
LOOPS_CAPTCHA_DRIVER=hcaptcha
HCAPTCHA_SITE_KEY=your-site-key
HCAPTCHA_SECRET_KEY=your-secret-key2FA is supported. No additional configuration required - users can enable it in their profile settings.
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;
}
}<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>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/cacheAdd to your crontab:
# Laravel Scheduler
* * * * * cd /var/www/loops && php artisan schedule:run >> /dev/null 2>&1php artisan config:cache
php artisan route:cache
php artisan view:cacheAdd 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=1Optimize 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- Disable debug mode in production (
APP_DEBUG=false) - Use HTTPS for all connections
- Regularly update dependencies
- Monitor logs for suspicious activity
- Use strong passwords and enable 2FA
- Keep FFmpeg updated for security patches
To update Loops:
# Pull latest changes
git pull origin main
# Update dependencies
composer install --no-dev --optimize-autoloader
# 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