PinePods
Overview
PinePods is a complete, self-hosted podcast management and streaming server written in Rust. You run one server for your household or team and subscribe to podcasts. Your subscriptions, listen history, playback position, downloads and settings then sync across every client: the built-in web player, desktop apps, mobile apps and a command-line client.
This SecureAnyCloud image runs the whole PinePods stack natively on a single VM. It has a full multi-user login system with an administrator account, per-user libraries, podcast search and discovery, episode downloads for offline listening, and a gpodder-compatible sync API so existing podcast apps can connect.
What you get:
- PinePods 0.9.0 (the official upstream runtime): the Rust
pinepods-apiserver, the Gogpodder-api, and the compiled web interface - nginx on port 80 serving the web interface and reverse-proxying the API to the Rust pinepods-api server (port 8032) and the gpodder-compatible endpoints to the Go gpodder-api (port 8042). In short, an nginx reverse proxy on
:80in front of the API on:8032and the gpodder API on:8042. - A PostgreSQL datastore (podcasts, episodes, users, history) and a local Valkey/Redis cache, both bound to the loopback interface
yt-dlpandffmpegfor episode downloads- A per-VM administrator password and PostgreSQL password, generated at first boot and stored in a root-only file, with the server URL resolved to the VM's public IP
- No seeded credential in the image. The admin account is created per VM on first boot.
pinepods-api.service,pinepods-gpodder.service,postgresql.service,redis-server.serviceandnginx.serviceas enabled and active systemd units- 24/7 SecureAnyCloud support
On the first boot of every deployed VM, a one-shot service generates a unique administrator password and a unique PostgreSQL password. It resolves the VM's public IP and writes it into the server's advertised URL, runs the database migrations, seeds the administrator account and starts the stack. The per-VM secrets are written to /root/pinepods-credentials.txt with mode 0600.
The shipped image carries an empty database with no seeded account. The administrator is created freshly on your VM's first boot, with a password that exists nowhere else.
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.
Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting size. Scale up for more users or heavy downloading.
Configure the network security group (NSG) to allow inbound 22/tcp from your management network. Also allow 80/tcp, plus 443/tcp once you enable HTTPS, from the networks your listeners will use to reach PinePods. Leave 8032 and 8042 closed. They are internal services reached only through nginx.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for PinePods by SecureAnyCloud, and select Create.
On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).
Select Review + create, then Create.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:pinepods-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name pinepods \
--image sac1647283583153:pinepods-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name pinepods --port 80 --priority 1010Step 3 - Connect to your VM
Connect over SSH:
ssh azureuser@<vm-public-ip>First-boot initialization completes in under a minute. By the time SSH is ready, the credentials file is in place and the site is serving.
Step 4 - Confirm the services are running
Check the five systemd units, the nginx/API/gpodder listeners, and the /api/health endpoint served through nginx:
systemctl is-active postgresql redis-server pinepods-api pinepods-gpodder nginx curl -fsS http://127.0.0.1/api/healthAll five services report active, and the health endpoint returns {"status":"healthy","database":true,"redis":true,...}. This proves that the full stack is serving: nginx, the Rust API, the gpodder API, PostgreSQL and Valkey/Redis.
nginx on :80 is the single public entry point. The API listens on :8032 and the gpodder API on :8042 behind it, with PostgreSQL and Valkey/Redis on loopback.
Next, check the running version and the health endpoint, and confirm that the per-VM credentials file is in place with root-only permissions:
cat /opt/pinepods/share/current_version curl -s http://127.0.0.1/api/health
sudo ls -l /root/pinepods-credentials.txtExpect PinePods 0.9.0, a health response that reports the database and Redis connections up, and a root-only (0600) per-VM credentials file.
Step 5 - Review the per-VM secrets
PinePods' administrator password and database password are generated uniquely on the first boot of your VM and written to a root-only file. Read it as root:
sudo cat /root/pinepods-credentials.txtThe file records the resolved site URL, the administrator username and password, and the local PostgreSQL login.
You can confirm the secure-by-default posture end to end from the same session. The rotated database password authenticates to PostgreSQL, and the per-VM administrator authenticates against the API and is issued an API key:
DBPASS=$(sudo grep '^pinepods.db.pass=' /root/pinepods-credentials.txt | cut -d= -f2-) PGPASSWORD="$DBPASS" psql -h 127.0.0.1 -U pinepods -d pinepods_database -tAc 'SELECT 1 AS db_auth_ok'
ADMU=$(sudo grep '^pinepods.admin.user=' /root/pinepods-credentials.txt | cut -d= -f2-)
ADMP=$(sudo grep '^pinepods.admin.pass=' /root/pinepods-credentials.txt | cut -d= -f2-)
curl -s -u "$ADMU:$ADMP" http://127.0.0.1/api/data/get_key | head -c 60; echoThe per-VM database password authenticates to PostgreSQL, and the per-VM administrator logs in through the API and is issued a session key. Both secrets are unique to this VM and exist in no other deployment.
Step 6 - Log in to the web interface
Open a web browser and go to http://<vm-public-ip>/. PinePods presents its login screen. Sign in with the per-VM administrator username and password from /root/pinepods-credentials.txt.
The first time you log in, PinePods walks you through a short Initial Setup where you pick your language, time zone and date/time formats. It then opens the home dashboard.
On the dashboard, Quick Links take you to your Saved episodes, Downloads, History, Feed and Podcasts. "This Week" shows your listening stats, and Top Podcasts surfaces your library.
Step 7 - Subscribe to podcasts
Use the Search New Shows box in the top bar to find podcasts by name (backed by the Podcast Index), or add a feed by its RSS URL. Then subscribe.
Your subscriptions appear under Podcasts, each showing its artwork and episode count. Open a podcast to browse and play episodes, download them for offline listening, or queue them up.
Subscriptions, playback position and history live in PostgreSQL on your server. Every client you sign in stays in sync: the web player, the desktop and mobile apps, and gpodder-compatible apps via the sync API.
Step 8 - Manage users and settings
Sign in as the administrator and open Settings. It gives you access to User Settings (themes, layout and playback preferences) and Admin Settings. Admin Settings cover user accounts, guest and self-service settings, the Podcast Index match, email and OIDC/SSO configuration, and server-wide options.
PinePods is multi-user. Add an account per person so that everyone gets their own library while sharing one server.
Step 9 - Inspect the library from the server (optional)
Everything a client shows is backed by PostgreSQL on the VM. You can inspect the library directly. For example, list the subscribed podcasts and their episode counts:
DBPASS=$(sudo grep '^pinepods.db.pass=' /root/pinepods-credentials.txt | cut -d= -f2-) PGPASSWORD="$DBPASS" psql -h 127.0.0.1 -U pinepods -d pinepods_database -P pager=off \
-c 'SELECT podcastname AS podcast, episodecount AS episodes FROM "Podcasts" ORDER BY podcastid'The subscribed podcasts and episode counts come straight from PostgreSQL, which shows that your server owns your library.
Security baseline
The image ships fully patched at capture time and keeps patching itself. Ubuntu's unattended-upgrades service is enabled, so security updates are installed automatically on your running VM.
The API, gpodder API, PostgreSQL and Valkey/Redis sit behind nginx, which is the single network-facing entry point. PostgreSQL and Valkey stay on loopback. Verify the state at any time:
systemctl is-enabled unattended-upgrades grep -h . /etc/apt/apt.conf.d/20auto-upgrades
sudo ss -tlnp | grep -E '127.0.0.1:(5432|6379) 'Keep only 22, 80 and, once HTTPS is enabled, 443 open in the NSG. Leave 8032 and 8042 closed so the API is reached only through nginx.
Enabling HTTPS
For any production deployment, serve PinePods over HTTPS so that logins and playback cannot be intercepted. The image ships with nginx, which certbot can configure automatically.
The following assumes a DNS record pointing your fully qualified domain name at the VM's public IP address, and 443/tcp open in the NSG:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d podcasts.your-domain.example \
--non-interactive --agree-tos -m you@your-domain.example \
--redirectAfter certbot finishes, point the server's advertised URL at the HTTPS address so that feed and asset links use it. Then restart the service:
sudo sed -i \
-e 's|^SERVER_URL=.*|SERVER_URL=https://podcasts.your-domain.example|' \
-e 's|^HOSTNAME=.*|HOSTNAME=https://podcasts.your-domain.example|' \
/etc/pinepods/pinepods.env
sudo systemctl restart pinepods-api.serviceBackup and maintenance
PinePods keeps all of its state in PostgreSQL: podcasts, episodes, users, history and settings. Back it up regularly:
sudo -u postgres pg_dump pinepods_database > <backup-dir>/pinepods-db-$(date +%F).sqlDownloaded episode files live under /opt/pinepods/downloads. If you keep offline downloads, include that directory in your file backups.
Keep the OS patched with sudo apt update && sudo apt upgrade. See the PinePods documentation at https://www.pinepods.online/docs/Introduction for upgrades and advanced configuration.
Support
This image is backed by 24/7 SecureAnyCloud support. Guaranteed response within 24 hours, one hour average for critical issues.
Contact sales@secureanycloud.com for help with deployment, user and authentication (OIDC/SSO) configuration, TLS termination, gpodder sync, upgrades and database administration.
For general PinePods questions, consult the documentation at https://www.pinepods.online/docs/Introduction. PinePods is licensed under the GNU GPL-3.0 and is a trademark of its respective owner. Use of the name here is nominative and does not imply affiliation or endorsement. All product and company names are trademarks or registered trademarks of their respective holders.
Deploy on Azure
Launch PinePods on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com