Skip to content

Instantly share code, notes, and snippets.

@brianonn
Last active March 20, 2026 05:32
Show Gist options
  • Select an option

  • Save brianonn/dcd4bff7569d470371042102ffdea824 to your computer and use it in GitHub Desktop.

Select an option

Save brianonn/dcd4bff7569d470371042102ffdea824 to your computer and use it in GitHub Desktop.
Playbook to build a AV1 encoder GPU VM on Digital Ocean
---
- 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
#!/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
#!/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