Step 1: Install Required Packages
Why: Installs Apache, PHP, and PHP-FPM (the fast process manager).
# Update package lists
sudo apt update
# Install software-properties-common (needed to add PPAs)
sudo apt install software-properties-common apt-transport-https -y
# Add Ondřej Surý's PPA for latest Apache
sudo add-apt-repository ppa:ondrej/apache2 -y
# Add Ondřej Surý's PPA for latest PHP (if not already added)
sudo add-apt-repository ppa:ondrej/php -y
# Update again to include the new repositories
sudo apt update
# Install Apache and PHP with extensions
sudo apt install apache2 php8.3 php8.3-fpm php8.3-common php8.3-mysql \
php8.3-xml php8.3-curl php8.3-gd php8.3-mbstring php8.3-zip \
php8.3-bcmath php8.3-intl php8.3-soap php8.3-opcache -y
# Enable required Apache modules for PHP-FPM (if using FPM)
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
# Restart Apache
sudo systemctl restart apache2
After install add this end of the apache2.conf in the /etc/apache2
# ----------------------------------------------------------------------
# MP4 Streaming Optimizations (Safe for Prefork + mod_php)
# ----------------------------------------------------------------------
# Use kernel-level file sending (critical for video)
EnableSendfile On
# Keep connections open for seeking
MaxKeepAliveRequests 0
KeepAliveTimeout 15
# Skip ETag calculation for static files
FileETag None
# Don't compress video files (they're already compressed)
<IfModule mod_deflate.c>
SetEnvIfNoCase Request_URI \.mp4$ no-gzip dont-vary
</IfModule>
# Browser caching for videos
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType video/mp4 "access plus 1 month"
</IfModule>
# Skip logging video requests (saves disk I/O)
SetEnvIf Request_URI "\.mp4$" dontlog
To restart both php and apache
sudo systemctl restart php8.3-fpm sudo systemctl restart apache2
Step 2: Enable Apache Modules
Why: Activates features for caching, compression, URL rewriting, SSL, and PHP-FPM connection.
sudo a2enmod headers expires rewrite ssl proxy proxy_fcgi setenvif
Step 3: Switch from Prefork to Event MPM
Why: Prefork = 1 process per user (slow, memory heavy). Event = 1 process handles many users (fast, efficient for streaming).
sudo a2dismod php8.3 sudo a2dismod mpm_prefork sudo a2enmod mpm_event sudo a2enconf php8.3-fpm
After that it is a good idea to setup the mpm_worker.conf in the /etc/apache2/mods-enabled
StartServers 3 MinSpareThreads 64 MaxSpareThreads 128 ThreadLimit 64 ThreadsPerChild 64 MaxRequestWorkers 256 MaxConnectionsPerChild 1000
Step 4: Start PHP-FPM
Why: PHP-FPM runs separately from Apache. Must be running or PHP won’t work.
sudo systemctl enable php8.3-fpm sudo systemctl start php8.3-fpm
/etc/php/8.3/apache2/php.ini- Used when PHP runs as an Apache module (mod_php)
- Handles PHP via Apache directly
/etc/php/8.3/fpm/php.ini- Used when PHP runs via PHP-FPM (FastCGI Process Manager)
- Handles PHP as a separate process that Apache communicates with
Step 5: Add Streaming Optimizations to /etc/apache2/apache2.conf
Why: Makes video streaming faster by using kernel-level file transfer, keeping connections open for seeking, and skipping unnecessary processing.
# Kernel sends file directly to network (bypasses Apache buffer)
EnableSendfile On
# Keeps connection open so user can seek without reconnecting
MaxKeepAliveRequests 0
KeepAliveTimeout 15
# Skips unnecessary file checksum calculation
FileETag None
# MP4 is already compressed, don't waste CPU compressing again
<IfModule mod_deflate.c>
SetEnvIfNoCase Request_URI \.mp4$ no-gzip dont-vary
</IfModule>
# Tells browser to cache videos for 1 month
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType video/mp4 "access plus 1 month"
</IfModule>
# Stops logging every video chunk request (saves disk I/O)
SetEnvIf Request_URI "\.mp4$" dontlog
CustomLog ${APACHE_LOG_DIR}/access.log combined env=!dontlog
Step 6: Virtual Host PHP Handler
Why: Tells Apache to send PHP files to PHP-FPM instead of trying to process them itself.
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>
Step 7: Restart Everything
Why: Applies all changes.
sudo systemctl restart php8.3-fpm sudo systemctl restart apache2
Step 8: Verify
Why: Confirms everything is working.
apachectl -V | grep -i mpm sudo systemctl status php8.3-fpm
Extras:
Install btop and iftop to watch resources and network use easier.
apt install iftop
btop need couple steps read their install docs
https://github.com/aristocratos/btop?tab=readme-ov-file#with-cmake-community-maintained
Disable gzip for videos since they are already encoded and compressed VirtualHost setup.
Disable logs since every network log will eat the I/O resource that server needs.
For security to block hotlinking and not allowing people t odownload your videos easily.
Headers setup in the vhost or in the cloudflare.
Header always set Access-Control-Allow-Origin "https://yoursite.com" Header always set Access-Control-Allow-Methods "GET, HEAD, OPTIONS" Header always set Access-Control-Allow-Headers "Content-Type, Range, Accept-Encoding" Header always set Access-Control-Expose-Headers "Content-Length, Content-Range, Accept-Ranges" Header always set Access-Control-Allow-Credentials "true"