MAATRIX / Блог / Подкастер платит за хостинг эпизодов: свой RSS и свои файлы

Подкастер платит за хостинг эпизодов: свой RSS и свои файлы

MAATRIX

Подкаст растёт, счёт от хостинга подкастов растёт вместе с ним — это стандартная модель специализированных сервисов: либо лимит по числу часов прослушивания в месяц, либо по числу активных эпизодов, либо и то и другое сразу. При этом сама RSS-лента и аудиофайлы физически лежат не у вас: сменить площадку — значит переносить фид, менять enclosure-ссылки в каждом эпизоде и рисковать, что подписчики в Apple Podcasts и Spotify эту ленту потеряют. Ниже — как собрать всё то же самое на своём сервере: хранение файлов, генерация RSS вручную и раздача аудио без посредника.

Что растёт вместе с аудиторией, а что нет

Логика тарифов у специализированных хостингов подкастов почти всегда завязана либо на трафик прослушиваний, либо на количество эпизодов в ленте. Пока подкаст маленький, это незаметно: несколько выпусков в месяц, скромная аудитория, минимальный тариф. Но стоит выпускам накопиться до сотни-другой или аудитории вырасти — тариф переходит на следующую ступень, и это происходит не потому, что вырос объём хранимых данных на диске (аудиофайлы за годы весят не так уж много), а потому что выросло количество запросов к файлам или число активных серий в фиде.

Второй момент — это не совсем ваш RSS. Формально лента доступна по прямой ссылке, и её можно скопировать в любой каталог подкастов. Но генерируется она движком хостинга, из его базы данных, по его правилам разбивки на страницы и его политике хранения старых выпусков. Если хостинг решит закрыться, сменить условия или заблокировать аккаунт — фид может исчезнуть вместе с историей выпусков, а восстанавливать ссылки на сотню эпизодов вручную, сверяя даты публикации и описания по скриншотам и локальным копиям файлов, — то ещё удовольствие.

Своя площадка снимает оба ограничения разом: диск на сервере стоит фиксированную сумму независимо от того, сколько раз выпуск скачали за месяц, а RSS-файл — это обычный текстовый файл, который лежит у вас и который вы контролируете полностью, вплоть до тегов, порядка сортировки и истории правок в git.

Что нужно, чтобы подкаст переехал на свой сервер

Набор минимальный и знакомый любому, кто разворачивал сайт на VPS:

  • Домен — можно тот же, что у сайта подкаста, поддомен вида feed.вашподкаст.ru, или отдельный. Если домена ещё нет, порядок регистрации на юрлицо и на физлицо описан в статье про регистрацию домена.
  • VPS — для аудио-подкаста не нужны серьёзные вычислительные мощности, это по сути файловый сервер плюс веб-сервер, отдающий статику. Значение имеют диск (аудиофайлы за несколько сезонов легко набегают на десятки гигабайт) и исходящий трафик, если аудитория заметная.
  • HTTPS-сертификат — подкаст-приложения (особенно Apple Podcasts) требовательны к валидности TLS на адресе фида и enclosure-ссылок. Let's Encrypt через certbot закрывает это бесплатно, пошагово — в статье про установку Let's Encrypt на VPS.
  • Веб-сервер — nginx отлично справляется с отдачей статичных mp3/m4a файлов и RSS-XML, поддерживает докачку (Range-запросы) из коробки.
  • Место для файлов и структура каталогов — простое дерево вида episodes/, feed.xml, assets/ (обложки).

Структура на диске может выглядеть так:

/srv/podcast/
├── feed.xml
├── episodes/
│   ├── s01e01-nazvanie-vypuska.mp3
│   ├── s01e02-nazvanie-vypuska.mp3
│   └── ...
├── assets/
│   └── cover.jpg
└── episodes.json      # метаданные для генератора ленты

Отдельный файл episodes.json с метаданными (заголовок, дата, длительность, описание, номер сезона/эпизода) удобнее, чем держать всё в голове или парсить имена файлов — он же становится источником истины при регенерации feed.xml.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

RSS-лента: как она устроена и как собрать её руками

Подкаст-RSS — это обычный RSS 2.0 с расширением itunes (де-факто стандарт, его понимают и Apple Podcasts, и Spotify, и большинство других каталогов). Ключевые элементы на уровне канала и на уровне эпизода:

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
     xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Название подкаста</title>
    <link>https://вашподкаст.ru</link>
    <language>ru</language>
    <description>Короткое описание подкаста для каталогов.</description>
    <itunes:author>Имя автора</itunes:author>
    <itunes:owner>
      <itunes:name>Имя автора</itunes:name>
      <itunes:email>you@вашподкаст.ru</itunes:email>
    </itunes:owner>
    <itunes:image href="https://feed.вашподкаст.ru/assets/cover.jpg"/>
    <itunes:category text="Technology"/>
    <itunes:explicit>false</itunes:explicit>

    <item>
      <title>S01E01 — Название выпуска</title>
      <description>Описание конкретного эпизода.</description>
      <pubDate>Fri, 22 Aug 2026 09:00:00 +0300</pubDate>
      <enclosure url="https://feed.вашподкаст.ru/episodes/s01e01-nazvanie-vypuska.mp3"
                 length="48213904"
                 type="audio/mpeg"/>
      <guid isPermaLink="false">podcast-s01e01</guid>
      <itunes:duration>00:52:14</itunes:duration>
      <itunes:explicit>false</itunes:explicit>
    </item>
  </channel>
</rss>

Здесь важны три вещи, которые часто ломают в самодельных лентах:

  1. length в enclosure — это точный размер файла в байтах, а не длительность. Берётся так: stat -c%s episodes/s01e01-nazvanie-vypuska.mp3. Если значение неверное или отсутствует, некоторые приложения-плееры некорректно показывают прогресс докачки.
  2. guid — уникальный и стабильный идентификатор эпизода. Если вы потом переименуете файл или поменяете URL, guid должен остаться прежним — иначе приложения подписчиков посчитают эпизод новым и он «всплывёт» повторно у всех в ленте.
  3. pubDate в формате RFC 822 — с явным часовым поясом. Разные библиотеки форматируют дату по-разному, но каталоги ожидают именно этот формат (Fri, 22 Aug 2026 09:00:00 +0300), а не ISO 8601.

Собирать этот XML вручную построчно для каждого выпуска утомительно и чревато опечатками в закрывающих тегах. Практичнее — маленький генератор, который читает episodes.json и рендерит feed.xml целиком при каждом обновлении.

Раздача аудио: nginx, докачка и кэш

Аудиофайлы в подкасте отдаются как обычная статика, но с нюансами: подкаст-приложения качают файл не всегда целиком за раз (докачка, фоновая загрузка, перемотка на середину эпизода), и им нужна поддержка Range-запросов. nginx поддерживает Range для статических файлов без дополнительной настройки — достаточно не проксировать файл через что-то, что эту поддержку обрезает.

Минимальный конфиг:

server {
    listen 443 ssl http2;
    server_name feed.вашподкаст.ru;

    ssl_certificate     /etc/letsencrypt/live/feed.вашподкаст.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/feed.вашподкаст.ru/privkey.pem;

    root /srv/podcast;

    # сама лента
    location = /feed.xml {
        default_type application/rss+xml;
        add_header Cache-Control "public, max-age=300";
    }

    # обложка и статичные картинки
    location /assets/ {
        add_header Cache-Control "public, max-age=86400";
    }

    # аудиофайлы эпизодов
    location /episodes/ {
        types { audio/mpeg mp3; audio/mp4 m4a; }
        add_header Cache-Control "public, max-age=31536000, immutable";
        add_header Accept-Ranges bytes;
        sendfile on;
        aio threads;
    }
}

Cache-Control: immutable для файлов эпизодов оправдан, если вы не перезаписываете файл по тому же имени после публикации (а вы не должны — при правках лучше выкладывать новый файл и обновлять enclosure, чем перетирать старый под тем же URL, иначе у части подписчиков в кэше браузера или CDN зависнет старая версия). Для feed.xml, наоборот, кэш короткий — 5 минут, — потому что лента обновляется при каждом новом выпуске и каталоги подкастов её периодически перепроверяют.

Если аудитория ощутимо распределена географически или счёт за трафик становится заметным, перед сервером можно поставить CDN — базовая настройка для статики описана в статье про подключение CDN. Для подкаста это особенно уместно: аудиофайлы — идеальный кандидат для кеширования, они не меняются после публикации.

Публикация эпизода: рабочий процесс и автоматизация

Ручное редактирование XML на каждый новый выпуск — плохая идея, там легко забыть закрывающий тег или ошибиться с датой. Практичнее держать метаданные в простом JSON и генерировать feed.xml скриптом при каждой публикации.

episodes.json:

[
  {
    "slug": "s01e01-nazvanie-vypuska",
    "title": "S01E01 — Название выпуска",
    "description": "О чём этот выпуск.",
    "pub_date": "2026-08-22 09:00:00 +0300",
    "file": "episodes/s01e01-nazvanie-vypuska.mp3",
    "duration": "00:52:14",
    "explicit": false
  }
]

Генератор на Python (использует библиотеку feedgen, ставится через pip install feedgen):

#!/usr/bin/env python3
import json
import os
from datetime import datetime
from feedgen.feed import FeedGenerator

BASE_URL = "https://feed.вашподкаст.ru"
ROOT = "/srv/podcast"

fg = FeedGenerator()
fg.load_extension("podcast")
fg.title("Название подкаста")
fg.link(href=f"{BASE_URL}", rel="alternate")
fg.description("Короткое описание подкаста для каталогов.")
fg.language("ru")
fg.podcast.itunes_author("Имя автора")
fg.podcast.itunes_category("Technology")
fg.podcast.itunes_image(f"{BASE_URL}/assets/cover.jpg")

with open(os.path.join(ROOT, "episodes.json"), encoding="utf-8") as f:
    episodes = json.load(f)

for ep in episodes:
    file_path = os.path.join(ROOT, ep["file"])
    size_bytes = os.path.getsize(file_path)

    fe = fg.add_entry()
    fe.id(ep["slug"])
    fe.title(ep["title"])
    fe.description(ep["description"])
    fe.enclosure(f"{BASE_URL}/{ep['file']}", str(size_bytes), "audio/mpeg")
    fe.podcast.itunes_duration(ep["duration"])
    fe.podcast.itunes_explicit("yes" if ep["explicit"] else "no")
    fe.pubDate(ep["pub_date"])

fg.rss_file(os.path.join(ROOT, "feed.xml"))
print("feed.xml обновлён,", len(episodes), "эпизодов в ленте")

Скрипт читает размер файла с диска сам (os.path.getsize), так что руками длину в байтах указывать не нужно — только длительность, её проще взять из ffprobe:

ffprobe -i episodes/s01e01-nazvanie-vypuska.mp3 -show_entries format=duration -v quiet -of csv="p=0"

Рабочий процесс публикации нового выпуска сводится к четырём шагам: залить mp3-файл в episodes/ по scp или rsync, добавить запись в episodes.json, прогнать генератор, убедиться, что feed.xml обновился (валидный XML можно проверить локально через xmllint --noout feed.xml). При желании это оборачивается в один bash-скрипт или git-хук, если метаданные версионируются в репозитории.

Бэкапы, домен и надёжность

У специализированного хостинга подкастов инфраструктурная надёжность — их забота: они держат бэкапы, следят за аптаймом, разбираются с DDoS. На своём сервере эти задачи переходят к вам, и это честная цена за полный контроль.

Практический минимум:

  • Резервная копия каталога episodes/, assets/ и episodes.json — обычный rsync на другой сервер или облачное хранилище раз в сутки. Файлы эпизодов не меняются после публикации, так что инкрементальный бэкап (тот же rsync с --link-dest или borg) практически не тратит место на повторных прогонах.
  • Домен держите отдельно от хостинг-провайдера сервера — это стандартная практика для любого self-hosted проекта, не только подкаста, и она развязывает вам руки при смене VPS.
  • Мониторинг самого факта, что feed.xml отдаётся и содержит актуальный список эпизодов — простая проверка по расписанию (curl -sf https://feed.вашподкаст.ru/feed.xml | xmllint --noout - в cron с уведомлением при ошибке) закрывает большинство сценариев «лента отвалилась, а вы узнали об этом от слушателей в комментариях».

Если подкаст уже давно живёт на стороннем хостинге, миграция не требует останавливать выпуск новых эпизодов: закачайте архив аудиофайлов, соберите episodes.json по истории (даты и заголовки обычно можно выгрузить из старого фида тем же xmllint или простым парсингом XML), сгенерируйте feed.xml и переключите ссылку на новый фид в тех местах, где вы её публиковали (сайт, соцсети, шоуноты). В самих каталогах (Apple Podcasts, Spotify) обычно есть механизм «редиректа фида» — старый URL отдаёт <itunes:new-feed-url>, и подписчики переезжают на новый адрес без потери истории прослушиваний. Учтите: у некоторых каталогов на это уходит не один день, так что старую ленту стоит оставить рабочей на переходный период, а не выключать сразу после переезда.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Обязательно ли использовать feedgen, или можно собирать XML вручную?

Можно и вручную — RSS с itunes-расширением это обычный XML, но при ручной сборке легко ошибиться в экранировании спецсимволов в описании эпизода (амперсанды, угловые скобки) или в закрывающих тегах. Библиотека или простой шаблонизатор (Jinja2 поверх XML-шаблона) снимает этот класс ошибок.

Нужен ли отдельный поддомен под фид или можно раздавать с основного сайта подкаста?

Можно с основного домена — принципиальной разницы нет, важно лишь, чтобы адрес был стабильным и не менялся при смене хостинга сайта. Отдельный поддомен (feed.вашподкаст.ru) удобнее тем, что раздачу статики и HTTPS для него можно настроить и обновлять независимо от остального сайта.

Что будет с уже собранной аудиторией в Apple Podcasts и Spotify при переезде?

Подписчики привязаны к RSS-URL, а не к хостингу файлов. Если вы аккуратно перенесёте фид (см. раздел про миграцию выше) и используете механизм редиректа, подписки и история сохраняются. Резкая смена адреса фида без редиректа — то, чего стоит избегать.

Сколько места на диске реально нужно под подкаст?

Зависит от битрейта записи и числа выпусков, точную цифру дать нельзя — но ориентировочно час аудио в приемлемом для голоса битрейте (96–128 kbps) занимает от полусотни до сотни с небольшим мегабайт, так что даже несколько сезонов еженедельного подкаста обычно укладываются в десятки гигабайт, а не сотни.

Что делать с транскриптами и заметками к выпускам?

Их можно хранить в том же episodes.json или в отдельных markdown-файлах рядом с аудио и отдавать через тот же nginx — это отдельная тема, если транскрипты делаются автоматически через распознавание речи, а не руками.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →