diff --git a/docs/project-work/deploy-na-railway.md b/docs/project-work/deploy-na-railway.md new file mode 100644 index 0000000..1c84acb --- /dev/null +++ b/docs/project-work/deploy-na-railway.md @@ -0,0 +1,135 @@ +--- +title: "Как задеплоить проект на Railway" +--- + +[Перейти на сайт](https://ru.hexlet.io) + +# Как задеплоить проект на Railway + +> Бесплатный деплой приложения из репозитория GitHub вместе с базой PostgreSQL без банковской карты. + +Railway собирает приложение прямо из репозитория на GitHub и выдаёт ему публичный адрес. Бесплатного тарифа хватает, чтобы показать проект наставнику, и карта для него не нужна. Инструкция проверена 11 ноября 2026 года на двух учебных приложениях. В [react-todo-app-with-backend](https://github.com/hexlet-components/react-todo-app-with-backend) фронтенд на React раздаёт сервер на Fastify, а [java-javalin-blog](https://github.com/hexlet-components/java-javalin-blog) это блог на Javalin с базой PostgreSQL. + +Как выбрать сервис и чем Railway отличается от остальных, рассказано в статье [Куда задеплоить учебный проект](kuda-deploit-proekt.md). + +## Что понадобится + +Нужен аккаунт на [GitHub](https://github.com/) и репозиторий с приложением. Аккаунт на Railway создаётся входом через GitHub, отдельная регистрация не нужна. + +## Подготовьте приложение + +Общие требования к приложению описаны в статье [Куда задеплоить учебный проект](kuda-deploit-proekt.md). Railway сам выбирает порт и передаёт его приложению в переменной `PORT`. + +Если у проекта есть фронтенд, его собирает скрипт `build` в `package.json`, а готовые файлы раздаёт сервер. Railway выполняет `build` перед запуском сам, поэтому собранную папку (`dist` у Vite) в репозиторий класть не нужно. Сервер запускается скриптом `start`. + +```json +{ + "scripts": { + "build": "vite build", + "start": "node bin/server.js" + } +} +``` + +Раздачу фронтенда сервером показывает [исходная статья в блоге Хекслета](https://ru.hexlet.io/blog/posts/railway). Там использован устаревший create-react-app, у Vite папка сборки называется `dist` вместо `build`, остальное работает так же. + +## Войдите и проверьте тариф + +Откройте [railway.com](https://railway.com/) и нажмите **Continue with GitHub**. Новый аккаунт получает пробный период, его хватает для деплоя. + +Если вы уже регистрировались на Railway раньше, пробный период мог закончиться. Тогда Railway не даст создать проект и напишет «Your trial has expired». Откройте **Settings → Plans** и нажмите **Downgrade to Free** внизу страницы. Тариф Free включается без карты. + +![Страница тарифов Railway после перехода на Free, 1 доллар кредитов в месяц, до 1 vCPU и 0,5 ГБ памяти на сервис, 0,5 ГБ хранилища](/img/docs/img-220--railway-free-plan.png) + +## Создайте проект + +На главной странице Railway нажмите **New** и выберите **GitHub Repository**. + +![Меню нового проекта в Railway, первый пункт GitHub Repository](/img/docs/img-221--railway-new-project.png) + +Начните вводить имя репозитория и выберите его в списке. + +![Поиск репозитория в Railway, по запросу react-todo найдены два репозитория](/img/docs/img-222--railway-select-repo.png) + +Если нужного репозитория в списке нет, у Railway нет к нему доступа. Очистите строку поиска, выберите **Configure GitHub App** и на странице GitHub дайте доступ к этому репозиторию. Достаточно отметить **Only select repositories** и выбрать один репозиторий. + +После выбора репозитория Railway создаёт сервис и запускает сборку. Если в корне репозитория лежит `Dockerfile`, Railway собирает образ по нему. Если нет, сам определяет Node.js, устанавливает зависимости, выполняет `build` и запускает `start`. Версию Node.js он берёт из поля `engines` в `package.json` или из файла `.nvmrc`. + +## Проверьте сборку + +Сборка занимает около минуты. Ход сборки показывает вкладка **Deployments**, а **View logs** открывает журналы. Во вкладке **Build Logs** видна установка зависимостей и сборка, во вкладке **Deploy Logs** вывод запущенного приложения. + +Сборка закончена, когда у деплоя стоит статус **ACTIVE**, а у сервиса **Online**. + +![Вкладка Deployments в Railway, деплой со статусом ACTIVE и подписью Deployment successful, сервис Online](/img/docs/img-223--railway-deployment-active.png) + +## Получите адрес + +Пока у сервиса нет адреса, из интернета он недоступен. Откройте **Settings**, найдите раздел **Networking** и нажмите **Generate Domain**. Railway выдаст адрес вида `<имя-сервиса>-production.up.railway.app`. + +![Раздел Networking в настройках сервиса, выданный домен на up.railway.app, порт 8080 и кнопка Generate Domain](/img/docs/img-224--railway-generate-domain.png) + +Откройте адрес в браузере. Если приложение работает, этот адрес отправляйте наставнику. + +![Учебное приложение Hexlet Todos, открытое по адресу на Railway, с добавленной задачей](/img/docs/img-225--railway-app-online.png) + +После каждого пуша в ветку `main` Railway пересобирает и перезапускает приложение сам. + +## Подключите базу PostgreSQL + +База на Railway работает отдельным сервисом в том же проекте, что и приложение. Откройте проект, нажмите **Add**, выберите **Database** и затем **PostgreSQL**. + +![Меню Database в Railway, первый пункт PostgreSQL](/img/docs/img-226--railway-add-postgres.png) + +Railway создаёт сервис Postgres вместе с томом `postgres-volume`, на котором лежат данные. Они сохраняются после перезапуска базы и после новых деплоев приложения. Мы проверили это перезапуском сервиса Postgres, и созданная перед этим запись осталась на месте. + +![Проект в Railway с тремя сервисами, Postgres с томом postgres-volume и два приложения](/img/docs/img-227--railway-postgres-and-app.png) + +Из интернета база недоступна, к ней подключаются только сервисы того же проекта. Адрес и пароль приложение получает через переменные окружения. Railway публикует у сервиса базы переменные `PGHOST`, `PGPORT`, `PGDATABASE`, `PGUSER`, `PGPASSWORD` и `DATABASE_URL`, а в переменной приложения на них ссылаются записью `${{Postgres.ИМЯ}}`. Переменные задаются во вкладке **Variables** сервиса приложения кнопкой **New Variable**. + +Многие библиотеки для Node.js и Python принимают `DATABASE_URL` как есть. Тогда в приложении достаточно завести переменную `DATABASE_URL` со значением `${{Postgres.DATABASE_URL}}`. + +Java-приложению нужен адрес в формате JDBC, а `DATABASE_URL` записан в формате `postgresql://`, поэтому JDBC-адрес собирают из отдельных переменных. В блоге на Javalin адрес читается из переменной `JDBC_DATABASE_URL`. + +```text +jdbc:postgresql://${{Postgres.PGHOST}}:${{Postgres.PGPORT}}/${{Postgres.PGDATABASE}}?user=${{Postgres.PGUSER}}&password=${{Postgres.PGPASSWORD}} +``` + +![Вкладка Variables сервиса приложения, заведена переменная JDBC_DATABASE_URL](/img/docs/img-228--railway-jdbc-variable.png) + +Сохраните переменную и запустите деплой кнопкой **Deploy** вверху проекта. В журнале запуска появится строка о подключении к базе, у блога это сообщение пула соединений HikariCP со словом `Start completed`. + +## Java-приложение + +Блог на Javalin собирается по `Dockerfile` из корня репозитория. Образ ставит JDK и Gradle, выполняет `gradle installDist` и запускает собранное приложение. Сборка заняла около полутора минут. + +Сервер Java-приложения тоже должен слушать порт из переменной `PORT` и адрес `0.0.0.0`. В блоге порт читается так. + +```java +String port = System.getenv().getOrDefault("PORT", "3000"); +``` + +Памяти тарифа Free хватает. Блог на Javalin после запуска занимал около 160 МБ из 0,5 ГБ, и настраивать JVM не понадобилось. Расход памяти показывает вкладка **Metrics** сервиса. Если график подходит к пределу, ограничьте кучу JVM переменной `JAVA_TOOL_OPTIONS` со значением `-XX:MaxRAMPercentage=60`. + +![Вкладка Metrics сервиса на Javalin, память около 160 МБ при пределе 0,5 ГБ](/img/docs/img-229--railway-java-metrics.png) + +![Блог на Javalin, открытый по адресу на Railway, со статьёй из базы PostgreSQL](/img/docs/img-230--railway-java-blog.png) + +Блог пишет журнал в поток ошибок, как и многие Java-приложения с логгером по умолчанию, поэтому Railway подсвечивает обычные строки `INFO` красным. Это не ошибка, смотрите на текст сообщения. + +## Ограничения бесплатного тарифа + +На тарифе Free сервису выделяется 1 vCPU и 0,5 ГБ памяти, а в месяц начисляется кредитов на 1 доллар. Этого хватает, чтобы показать учебный проект, но держать приложение постоянно не получится. Приложение без запросов засыпает, и первый запрос после паузы открывается дольше обычного. + +Тому базы данных на Free отводится 0,5 ГБ, учебной базе этого хватает с запасом. Каждый сервис расходует кредит отдельно, поэтому приложение с базой тратит его быстрее одного приложения. + +Число проектов на Free ограничено. Если Railway отвечает «Free plan resource provision limit exceeded», удалите ненужный проект в **Settings → Danger → Delete Project** у самого проекта. Свой домен на этом тарифе подключить нельзя. + +## Если что-то пошло не так + +Сначала откройте журналы деплоя, причина почти всегда написана там. + +- **Сервис падает сразу после старта, в журнале `Killed` и код 137.** Приложению не хватило 0,5 ГБ памяти. Так бывает, например, с базой данных, которая работает внутри процесса приложения, вроде PGlite. +- **Статус Online, а адрес не открывается.** Сервер слушает `127.0.0.1` или фиксированный порт. Проверьте `host: '0.0.0.0'` и `process.env.PORT`. +- **Фронтенд не открывается, а API отвечает.** Не выполнилась сборка фронтенда. Проверьте скрипт `build` в `package.json` и путь, по которому сервер раздаёт собранные файлы. +- **Railway пишет «Free plan resource provision limit exceeded».** Лимит проектов исчерпан. Удалите старый проект или добавьте сервис в существующий. diff --git a/docs/project-work/deploy-na-render.md b/docs/project-work/deploy-na-render.md new file mode 100644 index 0000000..40d9e48 --- /dev/null +++ b/docs/project-work/deploy-na-render.md @@ -0,0 +1,116 @@ +--- +title: "Как задеплоить проект на Render" +--- + +[Перейти на сайт](https://ru.hexlet.io) + +# Как задеплоить проект на Render + +> Деплой учебного проекта с базой PostgreSQL на бесплатный тариф Render. + +Render собирает приложение из репозитория на GitHub, запускает его и выдаёт адрес вида `<имя>.onrender.com`. На нём построены уроки о деплое в курсах Хекслета. Инструкция проверена 11 ноября 2026 года на учебном блоге [java-javalin-blog](https://github.com/hexlet-components/java-javalin-blog) с базой PostgreSQL на бесплатном тарифе. + +Если Render у вас не открывается или не даёт создать бесплатный сервис, выберите другой сервис по статье [Куда задеплоить учебный проект](kuda-deploit-proekt.md). + +## Что понадобится + +Нужен аккаунт на [GitHub](https://github.com/) и репозиторий с приложением. В Render удобнее всего войти через GitHub кнопкой **Continue with GitHub**. + +## Подготовьте приложение + +Общие требования к приложению описаны в статье [Куда задеплоить учебный проект](kuda-deploit-proekt.md). Render передаёт приложению порт в переменной `PORT`, по умолчанию это `10000` ([документация Render](https://render.com/docs/web-services#port-binding)). + +Для Node.js, Python, Ruby, Go, Rust и Elixir у Render есть готовая среда выполнения. В ней вы указываете команду сборки (**Build Command**) и команду запуска (**Start Command**), а версию языка можно закрепить переменной окружения, например `NODE_VERSION=24` для Node.js. Для Java, PHP и .NET готовой среды нет ([список сред](https://render.com/docs/native-runtimes)), такие приложения запускаются по `Dockerfile` из корня репозитория. + +Блог на Javalin собирается так. Образ ставит JDK и Gradle, выполняет `gradle installDist` и запускает собранное приложение. Полный файл лежит в [репозитории блога](https://github.com/hexlet-components/java-javalin-blog/blob/main/Dockerfile). + +```dockerfile +RUN gradle installDist + +CMD ["./build/install/java-javalin-blog/bin/java-javalin-blog"] +``` + +## Создайте базу данных + +Если приложению нужна база, создайте её первой. Нажмите **New** вверху страницы и выберите **Postgres**. + +![Меню New в Render с пунктами Web Service и Postgres](/img/docs/img-231--render-new-menu.png) + +Задайте имя базы в поле **Name**, остальные поля до региона можно не заполнять. Запомните регион (**Region**), сервис приложения нужно будет создать в том же регионе. + +Ниже, в разделе **Compute**, по умолчанию выбран платный тариф. Отметьте строку **$0 / month** с пометкой Free. Render предупредит, что бесплатная база удаляется через 30 дней. + +![Раздел Compute при создании базы, выбрана строка $0 в месяц с пометкой Free и предупреждением о 30 днях](/img/docs/img-232--render-postgres-free.png) + +Нажмите **Create database**. Через минуту-две в разделе **Connections** появятся параметры подключения. + +![Раздел Connections базы, имя хоста, порт, имя базы и пользователь, пароль и адреса ещё готовятся](/img/docs/img-233--render-postgres-connections.png) + +Приложению на Render нужен **Internal Database URL**, он работает внутри сети Render в том же регионе. **External Database URL** нужен для подключения с вашего компьютера, например через `psql`. + +## Создайте веб-сервис + +Нажмите **New** и выберите **Web Service**. На вкладке **Git Provider** выберите свой репозиторий, при первом входе Render попросит доступ к GitHub. Вкладка **Public Git Repository** принимает адрес любого публичного репозитория, но тогда Render не будет сам пересобирать приложение после пуша. + +Render определяет язык по файлам репозитория. Если в корне лежит `Dockerfile`, в поле **Language** будет стоять Docker. Регион выберите тот же, что у базы. + +![Поля Language, Branch и Region при создании веб-сервиса, выбраны Docker, ветка main и регион Frankfurt](/img/docs/img-234--render-web-docker-region.png) + +В разделе **Compute** по умолчанию тоже выбран платный тариф. Отметьте **$0 / month** с пометкой Free. + +![Раздел Compute при создании веб-сервиса, выбрана строка $0 в месяц с пометкой Free](/img/docs/img-235--render-web-free.png) + +## Передайте приложению строку подключения + +Render сам не передаёт приложению параметры базы, строку подключения нужно добавить в переменные окружения. Это можно сделать в разделе **Environment Variables** при создании сервиса или позже во вкладке **Environment** сервиса. + +Приложения на Node.js, Python и Rails обычно читают адрес из переменной `DATABASE_URL`. Скопируйте в неё Internal Database URL целиком. + +Java-приложению нужен адрес в формате JDBC, и он отличается от Internal Database URL. В JDBC логин и пароль передаются параметрами, а порт пишется явно. + +```text +Internal Database URL postgresql://USER:PASSWORD@HOST/DATABASE +JDBC jdbc:postgresql://HOST:5432/DATABASE?user=USER&password=PASSWORD +``` + +Блог на Javalin читает адрес из переменной `JDBC_DATABASE_URL`. Без неё он запускается на базе H2 в памяти, и данные пропадают при каждом перезапуске. + +![Вкладка Environment сервиса, переменная JDBC_DATABASE_URL со скрытым значением](/img/docs/img-236--render-env-variable.png) + +:::danger[Не добавляйте jdbc: к Internal Database URL] +Строка `jdbc:postgresql://USER:PASSWORD@HOST/DATABASE` выглядит правильной, но драйвер PostgreSQL принимает часть до `@` за адрес хоста. Приложение падает с ошибкой `JDBC URL invalid port number` и `No suitable driver`, а строка подключения вместе с паролем попадает в журнал Render. Если это случилось с настоящей базой, смените пароль. +::: + +## Запустите деплой + +Нажмите **Deploy web service**. Render скачает код, соберёт приложение и запустит его. Сборка блога на Javalin заняла меньше двух минут. Когда деплой закончится, в статусе появится **Deploy succeeded** с пометкой Live. + +![Страница деплоя, статус Deploy succeeded, Live, длительность 1 минута 44 секунды и журнал запуска Javalin](/img/docs/img-237--render-deploy-live.png) + +Журнал приложения открывается во вкладке **Logs**. По нему видно, к какой базе подключилось приложение. На снимке поиск по `HikariPool` показывает первый запуск на H2 и запуск после добавления `JDBC_DATABASE_URL`, когда пул соединений подключился к PostgreSQL. + +![Журнал Render с поиском HikariPool, сначала подключение jdbc:h2:mem:blog, затем org.postgresql.jdbc.PgConnection](/img/docs/img-238--render-postgres-logs.png) + +Адрес приложения указан на странице сервиса под его названием. Откройте его и проверьте, что приложение работает и сохраняет данные. + +![Блог на Javalin, открытый по адресу на Render, со статьёй из базы PostgreSQL](/img/docs/img-239--render-java-blog.png) + +После каждого пуша в подключённую ветку Render пересобирает приложение сам. Если сборка или запуск новой версии упали, Render оставляет работать предыдущую. + +## Ограничения бесплатного тарифа + +Бесплатный веб-сервис получает 0,1 CPU и 512 МБ памяти, а на все бесплатные сервисы рабочего пространства даётся 750 часов в месяц ([условия Free](https://render.com/docs/free)). Сервис засыпает через 15 минут без запросов. Первый запрос после сна ждёт запуска около минуты, Render показывает в это время страницу загрузки. Графики памяти и процессора на Free недоступны. + +Бесплатная база PostgreSQL занимает до 1 ГБ и живёт 30 дней. После этого даётся 14 дней на переход на платный тариф, затем база удаляется вместе с данными. В одном рабочем пространстве может быть только одна бесплатная база, поэтому перед новым проектом удалите старую. + +С бесплатного сервиса нельзя отправлять почту напрямую, исходящие соединения на порты 25, 465 и 587 закрыты. + +## Если что-то пошло не так + +Сначала откройте журнал деплоя, причина почти всегда написана там. + +- **Деплой упал со строкой `Exited with status 1`.** Приложение завершилось при запуске. Ошибка написана в журнале выше этой строки. +- **`JDBC URL invalid port number` и `No suitable driver`.** Адрес базы записан не в формате JDBC. Соберите его по шаблону из раздела про строку подключения. +- **Сборка прошла, а деплой не завершается.** Сервер слушает не тот порт или адрес. Проверьте `0.0.0.0` и `process.env.PORT` или его аналог в вашем языке. +- **Приложение не подключается к базе.** Проверьте, что сервис и база созданы в одном регионе и что используется Internal Database URL. +- **Данные пропадают после перезапуска.** Приложение работает на базе в памяти или в файле. Проверьте, что переменная с адресом PostgreSQL задана и приложение её читает. diff --git a/docs/project-work/kuda-deploit-proekt.md b/docs/project-work/kuda-deploit-proekt.md new file mode 100644 index 0000000..a528520 --- /dev/null +++ b/docs/project-work/kuda-deploit-proekt.md @@ -0,0 +1,51 @@ +--- +title: "Куда задеплоить учебный проект" +--- + +[Перейти на сайт](https://ru.hexlet.io) + +# Куда задеплоить учебный проект + +> Что подготовить в приложении перед деплоем и как выбрать сервис, если основной не работает. + +Во многих проектах Хекслета приложение нужно выложить в интернет, чтобы наставник открыл его по ссылке. Уроки о деплое построены на Render, но любой сервис может оказаться вам недоступен. В этой статье собрано то, что нужно сделать в приложении для любого сервиса, и сравнение сервисов, на которые можно задеплоить проект. + +## Подготовьте приложение + +Требования ниже общие для всех сервисов из этой статьи. Если приложение им отвечает, перенести его с одного сервиса на другой можно за несколько минут. + +**Сервер слушает порт из переменной `PORT` и адрес `0.0.0.0`.** Сервис сам выбирает порт и передаёт его приложению в переменной окружения. Многие фреймворки по умолчанию слушают только `127.0.0.1`, и снаружи такое приложение недоступно, даже когда в журнале всё в порядке. + +```javascript +const port = process.env.PORT || 3000; + +app.listen({ port, host: '0.0.0.0' }); +``` + +**Настройки и секреты приходят из переменных окружения.** Адрес базы данных, ключи и пароли не записываются в код и не попадают в репозиторий. Приложение читает их из окружения, например из `DATABASE_URL`, а значения задаются в настройках сервиса. + +**Сборка и запуск выполняются командами.** Сервис скачивает репозиторий и собирает приложение сам, поэтому собранные файлы, например папка `dist` фронтенда, в репозиторий не кладутся. Команды сборки и запуска обычно лежат в `package.json`, `Makefile` или `Dockerfile`. + +**Данные хранятся в базе.** Файлы внутри запущенного приложения пропадают при каждом перезапуске и новом деплое, как и база в памяти, например H2. Записи, которые должны сохраняться, держите в PostgreSQL. + +Перед деплоем проверьте локально, что приложение собирается и запускается теми же командами, которые выполнит сервис. Если не работает на вашем компьютере, на сервисе тоже не заработает. + +## Выберите сервис + +Начните с сервиса, на котором построен урок или проект. Если он не открывается, просит карту или не даёт создать бесплатный сервис, переходите к следующему в таблице. Данные в таблице проверены 11 ноября 2026 года. + +| | Render | Railway | +| --- | --- | --- | +| Карта для бесплатного тарифа | В [условиях Free](https://render.com/docs/free) не требуется | Не требуется для пробного периода и тарифа Free | +| Ограничения по регионам | Запрещено пользоваться из Крыма, Донецка, Луганска, Кубы, Ирана, Северной Кореи, Сирии и других регионов под санкциями США ([условия Render](https://render.com/terms)) | В [правилах Railway](https://railway.com/legal/acceptable-use) регионы не названы | +| Бесплатный веб-сервис | 512 МБ памяти, 750 часов в месяц на все сервисы, засыпает через 15 минут без запросов | 0,5 ГБ памяти, кредит 1 доллар в месяц на все сервисы, засыпает без запросов | +| Бесплатная база PostgreSQL | 1 ГБ, удаляется через 30 дней, одна на рабочее пространство | Том 0,5 ГБ, хранится, пока есть кредит | +| Подключение базы | Строку подключения копируют вручную | Сервис подставляет переменные базы сам | +| Java | Только через `Dockerfile` | Собирается автоматически по Gradle или Maven, подходит и `Dockerfile` ([Railpack](https://railpack.com/languages/java)) | +| Инструкция | [Деплой на Render](deploy-na-render.md) | [Деплой на Railway](deploy-na-railway.md) | + +Если в чек-листе проекта назван Render, а он у вас не работает, задеплойте проект на другой сервис из таблицы и напишите об этом наставнику при сдаче. + +## Если ни один сервис не работает + +Напишите наставнику, какие сервисы вы пробовали и что они ответили. Приложите сообщение об ошибке или снимок экрана. Наставник подскажет, как сдать проект. diff --git a/static/img/docs/img-220--railway-free-plan.png b/static/img/docs/img-220--railway-free-plan.png new file mode 100644 index 0000000..ae74f82 Binary files /dev/null and b/static/img/docs/img-220--railway-free-plan.png differ diff --git a/static/img/docs/img-221--railway-new-project.png b/static/img/docs/img-221--railway-new-project.png new file mode 100644 index 0000000..c17580e Binary files /dev/null and b/static/img/docs/img-221--railway-new-project.png differ diff --git a/static/img/docs/img-222--railway-select-repo.png b/static/img/docs/img-222--railway-select-repo.png new file mode 100644 index 0000000..1953fdc Binary files /dev/null and b/static/img/docs/img-222--railway-select-repo.png differ diff --git a/static/img/docs/img-223--railway-deployment-active.png b/static/img/docs/img-223--railway-deployment-active.png new file mode 100644 index 0000000..4001ac8 Binary files /dev/null and b/static/img/docs/img-223--railway-deployment-active.png differ diff --git a/static/img/docs/img-224--railway-generate-domain.png b/static/img/docs/img-224--railway-generate-domain.png new file mode 100644 index 0000000..30e0f0e Binary files /dev/null and b/static/img/docs/img-224--railway-generate-domain.png differ diff --git a/static/img/docs/img-225--railway-app-online.png b/static/img/docs/img-225--railway-app-online.png new file mode 100644 index 0000000..dbbda60 Binary files /dev/null and b/static/img/docs/img-225--railway-app-online.png differ diff --git a/static/img/docs/img-226--railway-add-postgres.png b/static/img/docs/img-226--railway-add-postgres.png new file mode 100644 index 0000000..2295bad Binary files /dev/null and b/static/img/docs/img-226--railway-add-postgres.png differ diff --git a/static/img/docs/img-227--railway-postgres-and-app.png b/static/img/docs/img-227--railway-postgres-and-app.png new file mode 100644 index 0000000..8b65fb8 Binary files /dev/null and b/static/img/docs/img-227--railway-postgres-and-app.png differ diff --git a/static/img/docs/img-228--railway-jdbc-variable.png b/static/img/docs/img-228--railway-jdbc-variable.png new file mode 100644 index 0000000..d0770a4 Binary files /dev/null and b/static/img/docs/img-228--railway-jdbc-variable.png differ diff --git a/static/img/docs/img-229--railway-java-metrics.png b/static/img/docs/img-229--railway-java-metrics.png new file mode 100644 index 0000000..c0a69b8 Binary files /dev/null and b/static/img/docs/img-229--railway-java-metrics.png differ diff --git a/static/img/docs/img-230--railway-java-blog.png b/static/img/docs/img-230--railway-java-blog.png new file mode 100644 index 0000000..02fa084 Binary files /dev/null and b/static/img/docs/img-230--railway-java-blog.png differ diff --git a/static/img/docs/img-231--render-new-menu.png b/static/img/docs/img-231--render-new-menu.png new file mode 100644 index 0000000..e0aa32a Binary files /dev/null and b/static/img/docs/img-231--render-new-menu.png differ diff --git a/static/img/docs/img-232--render-postgres-free.png b/static/img/docs/img-232--render-postgres-free.png new file mode 100644 index 0000000..26ea91f Binary files /dev/null and b/static/img/docs/img-232--render-postgres-free.png differ diff --git a/static/img/docs/img-233--render-postgres-connections.png b/static/img/docs/img-233--render-postgres-connections.png new file mode 100644 index 0000000..35e8176 Binary files /dev/null and b/static/img/docs/img-233--render-postgres-connections.png differ diff --git a/static/img/docs/img-234--render-web-docker-region.png b/static/img/docs/img-234--render-web-docker-region.png new file mode 100644 index 0000000..0996d22 Binary files /dev/null and b/static/img/docs/img-234--render-web-docker-region.png differ diff --git a/static/img/docs/img-235--render-web-free.png b/static/img/docs/img-235--render-web-free.png new file mode 100644 index 0000000..bc5fedb Binary files /dev/null and b/static/img/docs/img-235--render-web-free.png differ diff --git a/static/img/docs/img-236--render-env-variable.png b/static/img/docs/img-236--render-env-variable.png new file mode 100644 index 0000000..b78a233 Binary files /dev/null and b/static/img/docs/img-236--render-env-variable.png differ diff --git a/static/img/docs/img-237--render-deploy-live.png b/static/img/docs/img-237--render-deploy-live.png new file mode 100644 index 0000000..c473d84 Binary files /dev/null and b/static/img/docs/img-237--render-deploy-live.png differ diff --git a/static/img/docs/img-238--render-postgres-logs.png b/static/img/docs/img-238--render-postgres-logs.png new file mode 100644 index 0000000..c53b7f2 Binary files /dev/null and b/static/img/docs/img-238--render-postgres-logs.png differ diff --git a/static/img/docs/img-239--render-java-blog.png b/static/img/docs/img-239--render-java-blog.png new file mode 100644 index 0000000..d07141e Binary files /dev/null and b/static/img/docs/img-239--render-java-blog.png differ