We offer customization solutions and support ([email protected]) | Visit us on bitbyte3 for complete VOD solutions with apps.
NixStream
Installation

Manual Installation

Install NixStream on Ubuntu 22.04 without Docker

Manual install is for when you want full control over the stack. You run PHP, Nginx, MySQL, Redis, FFmpeg, and background workers yourself. Ubuntu 22.04 is the recommended baseline.

Shared hosting without shell access or queue workers will not work. NixStream needs Redis-backed queue workers for encoding and transcription jobs.

Want a faster path? Use the Docker install instead. This guide covers the same setup as the Docker image, on bare metal.

Before you start

You will need a VPS or dedicated server with:

  • Ubuntu 22.04 LTS
  • Root or sudo access
  • At least 4 CPU cores and 8 GB RAM
  • 100 GB SSD (more if you store a large video library locally)
  • Ports 80, 443, 1935, and 8888 available

Plan your domain and SSL early. Set APP_URL and KEY_SERVER_BASE_URL to the final HTTPS URL before going live.

Install flow

1. System packages

Update the system and install base dependencies:

sudo apt update && sudo apt upgrade -y

sudo apt install -y \
  nginx \
  mysql-server \
  redis-server \
  supervisor \
  git \
  curl \
  wget \
  unzip \
  zip \
  build-essential \
  ffmpeg \
  software-properties-common

Install PHP 8.2 and extensions:

sudo add-apt-repository ppa:ondrej/php -y
sudo apt update

sudo apt install -y \
  php8.2-fpm \
  php8.2-cli \
  php8.2-mysql \
  php8.2-redis \
  php8.2-mbstring \
  php8.2-xml \
  php8.2-curl \
  php8.2-zip \
  php8.2-gd \
  php8.2-intl \
  php8.2-bcmath \
  php8.2-opcache \
  php8.2-pcntl

Install Node.js 20 for the frontend build:

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

Install Composer:

curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

2. PHP upload limits

Video uploads need large PHP limits. Create /etc/php/8.2/fpm/conf.d/99-nixstream.ini:

upload_max_filesize = 10G
post_max_size = 10G
max_execution_time = 0
memory_limit = 2G

Apply the same file to the CLI config if you run artisan commands that process large files:

sudo cp /etc/php/8.2/fpm/conf.d/99-nixstream.ini /etc/php/8.2/cli/conf.d/99-nixstream.ini
sudo systemctl restart php8.2-fpm

3. Database and Redis

Create a MySQL database and user:

sudo mysql -e "CREATE DATABASE nixstream CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql -e "CREATE USER 'nixstream_user'@'localhost' IDENTIFIED BY 'your_strong_password';"
sudo mysql -e "GRANT ALL PRIVILEGES ON nixstream.* TO 'nixstream_user'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"

Verify Redis is running:

sudo systemctl enable redis-server
sudo systemctl start redis-server
redis-cli ping

You should see PONG.

4. Media binaries

NixStream relies on several binaries for transcoding and live streaming.

FFmpeg

Verify FFmpeg is installed:

ffmpeg -version
ffprobe -version

Shaka Packager

Download the packager binary:

sudo wget -q https://github.com/google/shaka-packager/releases/latest/download/packager-linux-x64 \
  -O /usr/local/bin/packager
sudo chmod +x /usr/local/bin/packager

Bento4 and NixStream CLIs

The project ships bin/media-vod, bin/media-live, and Bento4 tools in core/bin/. Make them executable:

cd /var/www/nixstream/core
chmod +x bin/media-vod bin/media-live bin/manifest
sudo cp bin/bento4/bin/* /usr/local/bin/

Whisper.cpp (transcription)

Build whisper.cpp and download a model:

cd /opt
sudo git clone --depth 1 --branch v1.7.5 --recurse-submodules https://github.com/ggml-org/whisper.cpp.git
cd whisper.cpp
sudo cmake -B build -DCMAKE_BUILD_TYPE=Release
sudo cmake --build build --config Release --target whisper-cli -j"$(nproc)"
sudo bash ./models/download-ggml-model.sh base

Note the paths for your .env:

WHISPER_CLI_PATH=/opt/whisper.cpp/build/bin/whisper-cli
WHISPER_MODELS_PATH=/opt/whisper.cpp/models

5. Application install

Clone or copy the project to /var/www/nixstream/core (adjust path as needed):

cd /var/www/nixstream/core
cp .env.example .env

Edit .env with your database, Redis, and URL settings:

APP_NAME="NixStream"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.com
FRONTEND_URL=https://your-domain.com
KEY_SERVER_BASE_URL=https://your-domain.com

DB_HOST=127.0.0.1
DB_DATABASE=nixstream
DB_USERNAME=nixstream_user
DB_PASSWORD=your_strong_password

QUEUE_CONNECTION=redis
CACHE_STORE=redis
SESSION_DRIVER=redis
REDIS_HOST=127.0.0.1

FFMPEG_BINARIES=/usr/bin/ffmpeg
FFPROBE_BINARIES=/usr/bin/ffprobe
SHAKA_PACKAGER_BINARIES=/usr/local/bin/packager

MEDIA_SERVER_HOST=http://127.0.0.1:8888
MEDIA_SERVER_INGEST_IP=your-server-public-ip

Install dependencies and build:

composer install --no-dev --optimize-autoloader
npm install
npm run build

php artisan key:generate
php artisan migrate --seed
php artisan storage:link

Set correct ownership:

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

Default admin after seeding:

  • Email: ADMIN_EMAIL from .env (defaults to [email protected])
  • Password: one-time temp password printed by ./install.sh or ./scripts/create-admin.sh (not stored in .env)

Change the password after first login.

6. Nginx configuration

Create /etc/nginx/sites-available/nixstream:

server {
    listen 80;
    server_name your-domain.com;
    root /var/www/nixstream/nixstream/public;
    index index.php index.html;

    client_max_body_size 10G;
    client_body_timeout 300s;
    client_header_timeout 300s;

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

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        try_files $uri =404;
    }

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

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_read_timeout 300;
    }

    location /storage/ {
        alias /var/www/nixstream/nixstream/storage/app/public/;
        expires 1y;
        add_header Cache-Control "public";
    }

    location /api/live/ {
        proxy_pass http://127.0.0.1:8888/;
        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;
    }

    location ~ /\. {
        deny all;
    }
}

Enable the site:

sudo ln -s /etc/nginx/sites-available/nixstream /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Add SSL with Certbot before going to production. See SSL setup.

7. Queue workers with Supervisor

Create /etc/supervisor/conf.d/nixstream-worker.conf:

[program:nixstream-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/nixstream/nixstream/artisan queue:work redis --queue=high,default,video-encoding --timeout=0 --memory=2048 --tries=3
directory=/var/www/nixstream/core
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/supervisor/nixstream-worker.log
stdout_logfile_maxbytes=100MB

Start the workers:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status

Both worker processes should show RUNNING.

8. Cron scheduler

Laravel needs a cron entry for scheduled tasks like recording watches:

sudo crontab -u www-data -e

Add:

* * * * * cd /var/www/nixstream/core && php artisan schedule:run >> /dev/null 2>&1

9. Live server

If you use live streaming, start the media-live binary. You can run it via Supervisor or systemd.

Example Supervisor config at /etc/supervisor/conf.d/nixstream-live.conf:

[program:nixstream-live]
command=/var/www/nixstream/nixstream/bin/media-live
directory=/var/www/nixstream/core
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/nixstream-live.log

Open firewall ports:

sudo ufw allow 1935/tcp
sudo ufw allow 8888/tcp

Set MEDIA_SERVER_API_KEY in .env. Docker install.sh generates one for you; on manual installs, pick a random secret and use the same value in the app and live server.

Verify health:

curl http://127.0.0.1:8888/health

10. Verify everything works

Run through this checklist:

  1. Open https://your-domain.com/panel and log in
  2. Upload a short test video and confirm encoding starts
  3. Check queue workers: sudo supervisorctl status
  4. Watch logs: tail -f storage/logs/laravel.log
  5. Confirm encoded files appear in storage/app/public/videos/
  6. If using live, push a test stream from OBS and confirm it shows as active

You can also run the project verify script:

cd /var/www/nixstream/core
./scripts/verify.sh

Common mistakes

Forgot php artisan storage:link Encoded videos and thumbnails won't be web-accessible.

Queue workers not running Uploads stay in "pending" forever. Check Supervisor status and Redis connectivity.

Wrong APP_URL Login, share links, and OAuth redirects break if the URL doesn't match what users see in the browser.

Live ingest IP not set OBS can't connect if MEDIA_SERVER_INGEST_IP points to localhost instead of your public IP.

PHP upload limits too low Large video uploads fail with 413 errors. Confirm both Nginx client_max_body_size and PHP post_max_size are set to 10G.

Permissions on storage/ Encoding jobs fail silently if www-data can't write to storage/.

From here: encoding profiles in the admin panel, S3 storage if media should live off-server, an API key for integrations, and monitoring for ongoing ops.

On this page