Last active
March 20, 2026 05:32
-
-
Save brianonn/dcd4bff7569d470371042102ffdea824 to your computer and use it in GitHub Desktop.
Playbook to build a AV1 encoder GPU VM on Digital Ocean
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| - name: Compile FFmpeg with NVENC AV1 for RTX 4000 | |
| hosts: localhost | |
| connection: local | |
| become: yes | |
| tasks: | |
| - name: Install build dependencies | |
| apt: | |
| name: | |
| - build-essential | |
| - yasm | |
| - nasm | |
| - cmake | |
| - libtool | |
| - libc6-dev | |
| - unzip | |
| - wget | |
| - libnuma1 | |
| - libnuma-dev | |
| - pkg-config | |
| - git | |
| - nvidia-cuda-toolkit | |
| state: present | |
| update_cache: yes | |
| - name: Add Graphics Drivers PPA | |
| apt_repository: | |
| repo: ppa:graphics-drivers/ppa | |
| state: present | |
| - name: Install latest NVIDIA 590 Driver and Encoding Libraries | |
| apt: | |
| name: | |
| - nvidia-driver-590 | |
| - libnvidia-encode-590 | |
| - nvidia-utils-590 | |
| - libnvidia-decode-590 | |
| state: present | |
| update_cache: yes | |
| - name: Clone NV-Codec-Headers | |
| git: | |
| repo: 'https://github.com/FFmpeg/nv-codec-headers.git' | |
| dest: /tmp/nv-codec-headers | |
| force: yes | |
| - name: Install NV-Codec-Headers | |
| make: | |
| chdir: /tmp/nv-codec-headers | |
| target: install | |
| - name: Clone FFmpeg source | |
| git: | |
| repo: 'https://git.ffmpeg.org/ffmpeg.git' | |
| dest: /tmp/ffmpeg | |
| depth: 1 | |
| - name: Configure, Build and Install FFmpeg | |
| shell: | | |
| ./configure --enable-nonfree --enable-cuda-nvcc --enable-libnpp \ | |
| --extra-cflags=-I/usr/local/cuda/include \ | |
| --extra-ldflags=-L/usr/local/cuda/lib64 \ | |
| --disable-static --enable-shared | |
| make -j$(( $(nproc) + 4 )) | |
| make install | |
| ldconfig | |
| args: | |
| chdir: /tmp/ffmpeg | |
| creates: /usr/local/bin/ffmpeg | |
| - name: Verify av1_nvenc encoder is available | |
| command: ffmpeg -hide_banner -encoders | |
| register: encoder_check | |
| changed_when: false | |
| failed_when: false | |
| - set_fact: | |
| av1_nvenc_available: "{{ 'av1_nvenc' in encoder_check.stdout }}" | |
| - name: Fail if encoder is missing | |
| fail: | |
| msg: "FAIL: av1_nvenc encoder not available in ffmpeg!" | |
| when: not av1_nvenc_available | |
| - name: Success if encoder is available | |
| debug: | |
| msg: "Success! AV1 Encoder is available in ffmpeg" | |
| - name: Run A real hardware AV1 test encode | |
| shell: | | |
| ffmpeg -y -f lavfi -i testsrc=duration=10:size=1920x1080:rate=30 \ | |
| -c:v av1_nvenc -pix_fmt yuv420p10le -preset p4 /tmp/test_verify.mp4 | |
| register: test_encode | |
| changed_when: false | |
| failed_when: false | |
| - set_fact: | |
| av1_encode_success: "{{ test_encode.rc == 0 }}" | |
| - name: Confirm encoding success | |
| debug: | |
| msg: "AV1 Hardware encoding verified successfully!" | |
| when: av1_encode_success | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| set -e | |
| # Paste this into the cloud-init startup scripts (i.e. on DigitalOcean) | |
| # 1. Install Ansible and Git | |
| apt-get update | |
| apt-get install -y git software-properties-common | |
| apt-add-repository --yes --update ppa:ansible/ansible | |
| apt-get install -y ansible | |
| # 2. Download and run the playbook | |
| MYGIST="https://gist.githubusercontent.com/brianonn/dcd4bff7569d470371042102ffdea824" | |
| PLAYBOOK_URL="${MYGIST}/raw/av1-gpu-setup.yaml" | |
| mkdir -p /root/ffmpeg-setup | |
| cd /root/ffmpeg-setup | |
| wget "$PLAYBOOK_URL" -q -O playbook.yml | |
| # 3. Execute locally | |
| ansible-playbook playbook.yml -c local |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| #set -vx | |
| # set TOKEN | |
| if [ -z "$TOKEN" ]; then | |
| echo "Set the environment variable 'TOKEN' first" | |
| exit 1 | |
| fi | |
| NL=" | |
| " | |
| MYGIST="https://gist.githubusercontent.com/brianonn/dcd4bff7569d470371042102ffdea824" | |
| USERDATA_URL="${MYGIST}/raw/cloud-init.sh" | |
| USERDATA="#!/bin/sh${NL}wget -q -O- ${USERDATA_URL} | /bin/bash ${NL}" | |
| RAND="$(dd if=/dev/urandom bs=4k count=1 2>/dev/null | tr -dc 'a-z' | fold -b -w 5 | head -1)" | |
| NAME="ffmpeg-${RAND}" | |
| SSHKEYS="[37588323,34753893]" # my key IDs | |
| GPU=1 | |
| if [[ $GPU == 1 ]]; then | |
| REGION="tor1" | |
| VPC_UUID="6eb22252-276f-4380-9d21-8783e406da7b" # tor1 default VPC | |
| SIZE="gpu-6000adax1-48gb" | |
| IMAGE="ubuntu-22-04-x64" | |
| else | |
| REGION="sfo1" | |
| VPC_UUID="07624deb-6207-46f5-be89-7eaea5d7159f" # sfo1 default VPC | |
| SIZE="c-48" | |
| IMAGE="ubuntu-22-04-x64" | |
| fi | |
| RESPONSE=$(curl -s \ | |
| -X POST \ | |
| -H 'Content-Type: application/json' \ | |
| -H "Authorization: Bearer ${TOKEN}" \ | |
| -d \ | |
| '{ | |
| "name":"'${NAME}'", | |
| "region":"'${REGION}'", | |
| "size":"'${SIZE}'", | |
| "image":"'${IMAGE}'", | |
| "ssh_keys":'${SSHKEYS}', | |
| "backups":false, | |
| "ipv6":false, | |
| "monitoring":true, | |
| "tags":["gpu"], | |
| "user_data":"'"${USERDATA}"'", | |
| "vpc_uuid":"'${VPC_UUID}'" | |
| }' \ | |
| https://api.digitalocean.com/v2/droplets ) | |
| # check for success, look for the droplets key in the RESPONSE | |
| if echo "$RESPONSE" | jq -e 'has("droplet")' >/dev/null; then | |
| DROPLET_ID=$(echo "$RESPONSE" | jq -r '.droplet.id') | |
| echo "Success! Droplet ID: $DROPLET_ID" | |
| else | |
| ERROR_MSG=$(echo "$RESPONSE" | jq -r '.message // "Unknown API Error"') | |
| echo "Error: $ERROR_MSG" | |
| exit 1 | |
| fi | |
| echo "Waiting for IP address for Droplet $DROPLET_ID.." | |
| while true; do | |
| # Query the specific droplet endpoint | |
| RESPONSE=$(curl -s -X GET \ | |
| -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer $TOKEN" \ | |
| "https://api.digitalocean.com/v2/droplets/$DROPLET_ID") | |
| # Extract the public IPv4 address using jq | |
| IP_ADDRESS=$(echo $RESPONSE | jq -r '.droplet.networks.v4[] | select(.type == "public") | .ip_address') | |
| # If the IP is found and not null, break the loop | |
| if [ "$IP_ADDRESS" != "null" ] && [ -n "$IP_ADDRESS" ]; then | |
| echo "Success! Droplet IP: $IP_ADDRESS" | |
| break | |
| fi | |
| echo "Still waiting..." | |
| sleep 5 | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment