Когда я начал поднимать PostgreSQL через Docker для своих проектов, всё выглядело просто: описал сервис в docker-compose.yml, запустил контейнер - база доступна.

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

С опытом я пришел к более удобному варианту. Сначала запускал сам контейнер с базой и уже далее делал миграции. Так проще контролировать процесс, легче отлаживать ошибки и понятнее, что именно происходит с базой. Для миграций в своих Go-проектах я использовал goose.

goose - это инструмент для работы с миграциями базы данных. Его можно использовать как CLI-утилиту из терминала или подключить как Go-пакет и запускать миграции из кода. В этой статье я покажу вариант с запуском миграций через CLI-утилиту.

Для начала установим goose как CLI-утилиту:

go install github.com/pressly/goose/v3/cmd/goose@latest

После установки можно проверить, что команда доступна:

goose --version

Далее рассмотрим такую структуру проекта и покажу где я буду хранить миграции и как создать файлы для миграций:

my-app/
├── cmd/
│   └── app/
│       └── main.go
├── migrations/
├── docker-compose.yml
├── Makefile
├── go.mod
└── go.sum

В папке migrations будут лежать SQL-файлы миграций. Каждый такой файл описывает изменение схемы базы: создание таблицы, добавление колонки, создание индекса и так далее.

goose -dir migrations create create_users sql

Флаг -dir указывает папку, где нужно создать файл миграции. create_users — это название миграции, а sql означает, что миграция будет написана обычным SQL.

В docker-compose.yml опишем вот такой контейнер для запуска PostgreSQL:

services:
  postgres:
    image: postgres:16-alpine
    container_name: go_postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: app_db
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Сам запуск контейнера здесь подробно разбирать не буду: для этого достаточно выполнить docker compose up -d. Основной фокус статьи — запуск миграций. Запускать свои миграции я буду через Makefile вот так он выглядит внутри:

DB_URL=postgres://postgres:postgres@localhost:5432/app_db?sslmode=disable
MIGRATIONS_DIR=migrations

.PHONY: migrate-up migrate-down migrate-status migrate-create

migrate-up:
	goose -dir $(MIGRATIONS_DIR) postgres "$(DB_URL)" up

migrate-down:
	goose -dir $(MIGRATIONS_DIR) postgres "$(DB_URL)" down

migrate-status:
	goose -dir $(MIGRATIONS_DIR) postgres "$(DB_URL)" status

migrate-create:
	goose -dir $(MIGRATIONS_DIR) create $(name) sql

В моём случае goose запускается с хост-машины, поэтому в DB_URL я использую localhost. Если запускать миграции из другого контейнера внутри docker-compose, вместо localhost нужно будет указать имя сервиса — postgres.

Далее после запуска нашего контейнера базы данных мы уже применить наши миграции. и мы просто должны написать:

make migrate-up

После запуска можете проверить статус:

make migrate-status

Теперь миграции запускаются отдельным шагом после старта PostgreSQL. Поэтому мы не попадаем в ситуацию, когда goose пытается подключиться к базе раньше, чем она готова принимать соединения..

Комментарии (2)


  1. dem0n3d
    29.05.2026 06:59

    Сначала запускал сам контейнер с базой и уже далее делал миграции.

    depends_on:
      service:
        condition: service_healthy

    В compose можно сделать так. Сервис-мигратор при этом будет ожидать работоспособности базы данных и только потом стартовать.


    1. albatomm Автор
      29.05.2026 06:59

      Да, тоже хорошая практика. В целом и этот способ тоже можно было бы в статье описать.