# Docker на VPS: установка и первый контейнер в Ubuntu 24.04

> Ставим Docker Engine из официального репозитория, запускаем первый контейнер nginx, привязываем порт к localhost и разбираем, почему Docker обходит ufw.

- Рубрика: Гайды по играм
- Игра: VPS
- Опубликовано: 2026-11-13T19:00:00+03:00
- Обновлено: 2026-11-13T19:00:00+03:00
- Адрес: https://blog.egnetwork.ru/vps-docker-pervyj-kontejner
- Автор: Команда EGN

## Коротко

Docker запускает приложения в изолированных контейнерах. На VPS это удобно: каждый сервис живёт отдельно, его легко обновить или удалить, а сервер остаётся чистым. Установите Docker Engine из официального репозитория `download.docker.com`, проверьте его командой `sudo docker run hello-world` и запустите первый контейнер, например nginx. Порт публикуйте так: `-p 127.0.0.1:8080:80`. Тогда сайт доступен только с самого сервера, а наружу его выведет reverse proxy.

Главная ловушка Docker на сервере — он работает с iptables напрямую и может открыть порт мимо правил ufw. Это не ошибка, а свойство Docker, и с ним нужно считаться.

## Что такое Docker и зачем он на VPS

Контейнер — это процесс с собственным набором файлов, библиотек и сетевых правил. Образ — шаблон, из которого контейнер создаётся. Например, образ `nginx:stable` содержит веб-сервер в готовом виде, и вам не нужно ставить его в систему.

Польза на VPS состоит из трёх вещей. Во-первых, приложения не конфликтуют: одна версия PHP не мешает другой. Во-вторых, обновление сводится к замене образа. В-третьих, удаление сервиса не оставляет мусора в системе. Цена за это — немного сложности и необходимость понимать сети Docker.

Если вы хотите поднять несколько сервисов сразу, например сайт, базу и бота, посмотрите статью про [Docker Compose](https://blog.egnetwork.ru/vps-docker-compose-neskolko-servisov). Здесь разбираем первый контейнер.

## Шаг 1. Устанавливаем Docker Engine

Официальная инструкция Docker для Ubuntu использует отдельный репозиторий. Установка из стандартного репозитория Ubuntu тоже работает, но версии там могут отставать. Сначала подготовьте ключ и источник:

```bash
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
```

Затем добавьте источник в формате deb822. Команда ниже взята из официальной инструкции Docker для Ubuntu (проверено в ноябре 2026: поддерживаются 22.04, 24.04 и 26.04). Кодовое имя выпуска и архитектура подставляются автоматически:

```bash
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update
```

Если у вас Debian, адрес и ключ другие (`download.docker.com/linux/debian`), смотрите отдельную страницу инструкции для Debian. Теперь установите пакеты:

```bash
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

Пакет `docker-compose-plugin` даёт команду `docker compose`, которая понадобится позже. Служба запускается сама. Проверить состояние можно так:

```bash
sudo systemctl status docker
```

## Шаг 2. Проверяем установку

Тестовый образ `hello-world` скачивается, запускается и выводит сообщение. Это самый короткий способ убедиться, что Docker работает:

```bash
sudo docker run hello-world
```

В выводе должна быть строка `Hello from Docker!` и пояснение, что установка работает правильно. Если её нет, смотрите ошибку: чаще всего это нехватка места на диске или отсутствие доступа к `download.docker.com`.

![Установка Docker Engine](https://blog.egnetwork.ru/media/619)

## Шаг 3. Запускаем первый контейнер

Запустим nginx в фоне и привяжем его порт 80 внутри контейнера к порту 8080 на сервере:

```bash
sudo docker run -d --name web -p 127.0.0.1:8080:80 nginx:stable
```

Разберём параметры. `-d` запускает контейнер в фоне. `--name web` задаёт имя, по которому его удобно найти и остановить. `-p 127.0.0.1:8080:80` публикует порт 80 контейнера на адрес `127.0.0.1` сервера, порт 8080. Именно адрес `127.0.0.1` ограничивает доступ: снаружи сервера этот порт не виден.

Проверьте, что контейнер работает, и ответ приходит:

```bash
sudo docker ps --format 'table {{.Names}}\t{{.Ports}}'
curl -I http://127.0.0.1:8080
```

Команда `docker ps` покажет имя и порты. `curl` должен вернуть заголовок `HTTP/1.1 200 OK`.

![Первый контейнер и проверка порта](https://blog.egnetwork.ru/media/620)

> [!WARNING] Не публикуйте порт без адреса
> Запись `-p 8080:80` без `127.0.0.1` открывает порт на всех интерфейсах. Такой порт доступен из интернета, даже если в ufw его нет. Для сервисов, которые должны работать только для сайта, всегда указывайте `127.0.0.1`.

## Шаг 4. Выводим сайт в интернет через reverse proxy

Контейнер на `127.0.0.1:8080` не виден снаружи, и это правильно. Публичный вход делает reverse proxy, например nginx на хосте, который проксирует запросы по домену на контейнер. Так вы получаете один публичный вход с HTTPS, а контейнеров может быть несколько. Настройку reverse proxy и сертификата разбираем в статьях про [сайт на VPS с nginx и Let's Encrypt](https://blog.egnetwork.ru/vps-sait-nginx-letsencrypt). Ключевой момент здесь простой: публикуйте контейнеры на localhost, а наружу выводите один сервис.

## Примеры: типичные команды

```bash
sudo docker images
sudo docker logs web
sudo docker stop web
sudo docker rm web
sudo docker run -d --name web -p 127.0.0.1:8080:80 nginx:stable
```

Первая команда показывает образы на диске. Вторая выводит журнал контейнера, полезно при ошибках. Третья и четвёртая останавливают и удаляют контейнер. Последняя создаёт его заново.

Обновление nginx до свежей версии выглядит так: `docker pull nginx:stable`, затем остановка, удаление и новый запуск контейнера. Данные, которые нужно сохранить, должны лежать в томах, а не внутри контейнера, иначе при удалении они пропадут.

Чтобы контейнер поднимался сам после перезагрузки сервера, добавьте политику перезапуска:

```bash
sudo docker run -d --name web --restart unless-stopped -p 127.0.0.1:8080:80 nginx:stable
```

Политика `unless-stopped` перезапускает контейнер после сбоя и после старта Docker, кроме случая, когда вы остановили его сами командой `docker stop`.

## Обход ufw: что нужно знать

Docker создаёт собственные правила iptables для публикации портов. Пакеты для опубликованных портов направляются через таблицу `nat` и обходят цепочки INPUT, которыми пользуется ufw, поэтому правила ufw на них не действуют. В документации Docker прямо сказано, что Docker и ufw несовместимы в этом месте и что вручную менять правила Docker не стоит. Практический вывод: порт, опубликованный без `127.0.0.1`, будет доступен из интернета, даже если вы его не разрешали в ufw.

Из этого следуют три практических правила:

- публикуйте порты только на `127.0.0.1`, если сервис не должен быть публичным;
- не полагайтесь на ufw как на единственную защиту для портов Docker;
- после запуска нового контейнера проверяйте доступ снаружи, например с другого устройства;
- на VPS EGN добавьте внешнюю защиту: во вкладке «Фаервол» в карточке услуги включите режим «Свои правила» и оставьте только нужные порты. Такой фильтр работает до сервера, и порт, который Docker открыл мимо ufw, до контейнера не дойдёт.

Подробнее про базовую защиту читайте в статье [Базовая защита VPS: ufw, fail2ban и обновления](https://blog.egnetwork.ru/vps-bazovaya-zashchita).

## Частые ошибки

**Добавили пользователя в группу docker и забыли про риск.** Группа `docker` даёт права, близкие к root: контейнер может смонтировать корень хоста. Выдавайте её только доверенным пользователям. Для повседневной работы часто достаточно `sudo docker`.

**Порт занят.** Если 8080 уже используется, Docker сообщит об ошибке. Посмотрите, кто слушает порт, командой `sudo ss -tlnp | grep 8080`, и выберите другой.

**Данные пропали после удаления контейнера.** Данные внутри контейнера не переживают `docker rm`. Используйте именованные тома: `-v web-data:/usr/share/nginx/html`.

**Не работает сайт после обновления образа.** Проверьте журнал `sudo docker logs web` и конфиг, который вы монтируете. Новая версия образа может изменить пути.

## Пример: два контейнера на одном VPS

Чаще всего на сервере работает не один контейнер. Например, сайт и административная панель, которые должны жить рядом, но не конфликтовать. Каждый получает собственное имя и собственный порт на localhost:

```bash
sudo docker run -d --name site -p 127.0.0.1:8080:80 nginx:stable
sudo docker run -d --name admin -p 127.0.0.1:8081:80 nginx:stable
sudo docker ps --format 'table {{.Names}}\t{{.Ports}}'
```

Имена `site` и `admin` нужны, чтобы потом останавливать контейнеры по понятному названию. Порты 8080 и 8081 разные, поэтому конфликта нет. Снаружи оба недоступны, а nginx на хосте проксирует домены на нужный порт. Так вы получаете два сервиса за одним публичным входом.

## Пример: место на диске

Docker хранит образы, слои и тома в `/var/lib/docker`, и на небольшом VPS этот каталог растёт незаметно. Посмотреть, сколько места занимают образы, контейнеры и тома, можно командой:

```bash
sudo docker system df
```

Неиспользуемые образы удаляются командой `sudo docker image prune`. Она спросит подтверждение и не тронет образы, которые нужны запущенным контейнерам. Удалять тома без проверки не стоит, потому что в них могут лежать данные базы или файлы сайта. Перед очисткой всегда смотрите список через `sudo docker volume ls`.

## Таблица: симптом, причина и решение

| Симптом | Причина | Решение |
|---|---|---|
| `permission denied while trying to connect to the Docker daemon` | Команда без `sudo`, пользователь не в группе docker | Используйте `sudo` или добавьте пользователя в группу осознанно |
| `port is already allocated` | Порт занят другой программой или контейнером | Найдите процесс через `ss -tlnp` и выберите другой порт |
| Контейнер сразу завершается | Ошибка в приложении или не хватает переменных | Смотрите `sudo docker logs имя` |
| Порт открыт из интернета, хотя ufw его не пускает | Публикация без `127.0.0.1` | Пересоздайте контейнер с `-p 127.0.0.1:...` |
| `docker: command not found` после установки | Пакет не установился до конца | Повторите `apt install`, проверьте ошибки |

## Вопросы и ответы

### Нужен ли Docker, если сервис можно поставить напрямую?

Не обязательно. Прямая установка проще для одного сервиса. Docker выигрывает, когда сервисов несколько или версии зависимостей конфликтуют.

### Можно ли использовать Docker на небольшом VPS?

Да, но учитывайте, что сам Docker и образы занимают место на диске и память. Следите за диском командой `df -h /`.

### Как удалить Docker полностью?

Остановите и удалите контейнеры и образы, затем удалите пакеты через `apt remove` и очистите каталог `/var/lib/docker`. Перед этим сохраните данные из томов.

## Итог

Установите Docker Engine из официального репозитория, проверьте `hello-world`, запустите nginx и публикуйте порты только на `127.0.0.1`. Так вы получаете изоляцию сервисов без лишних публичных портов. Помните про обход ufw и регулярно проверяйте, что открыто наружу. Дальше полезно разобраться с [мониторингом ресурсов VPS](https://blog.egnetwork.ru/vps-monitoring-resursov), потому что контейнеры расходуют память и диск.

Сервер под VPS за пару минут — [egnetwork.ru](https://egnetwork.ru/)

## Официальные источники

- [download.docker.com](https://download.docker.com/linux/ubuntu/gpg)
- [download.docker.com](https://download.docker.com/linux/ubuntu)
- [127.0.0.1](http://127.0.0.1:8080/)

---

Материал EGN (blog.egnetwork.ru), обновлено 13 ноября 2026; при цитировании указывайте ссылку.
