> ## Content Index
> Fetch the complete content index at: https://mlog.me/llms.txt
> Use this file to discover other available public pages before exploring further.

# Docker Compose 네트워크 분리: Nginx Proxy Manager·Ghost·MySQL을 공용망과 내부망으로 나누기
- URL: https://mlog.me/docker-compose-network-isolation-npm-ghost-mysql/
- Published: 2026-09-01T05:42:37.000Z
- Updated: 2026-09-01T05:42:37.000Z
- Description: Nginx Proxy Manager는 Ghost 프록시망에, MySQL은 내부망에만 연결하고 호스트 포트 게시를 제거하는 Docker Compose 구성과 검증·롤백 순서입니다.
- Author: mLog
- Tags: Docker, Nginx Proxy Manager, Ghost, #MySQL, 보안

셀프호스팅 스택을 처음 만들 때는 연결부터 성공시키기 위해 Ghost의 `2368`, MySQL의 `3306`을 호스트에 게시하기 쉽다.

```yaml
ports:
  - "2368:2368"

ports:
  - "3306:3306"

```

하지만 Nginx Proxy Manager와 Ghost가 같은 Docker 호스트에서 실행된다면 NPM은 Docker 네트워크를 통해 Ghost의 컨테이너 포트로 직접 연결할 수 있다. Ghost와 MySQL도 같은 백엔드 네트워크에서 `db:3306`으로 통신한다. 이 경우 Ghost와 MySQL의 포트를 호스트 전체에 게시할 이유가 없다.

이 글의 목표는 통신 경로를 다음처럼 나누는 것이다.

- **공용 프록시망:** NPM과 Ghost만 연결한다.
- **내부 데이터망:** Ghost와 MySQL만 연결한다.
- **인터넷 공개:** NPM의 HTTP·HTTPS만 허용한다.
- **직접 비공개:** Ghost 2368과 MySQL 3306은 호스트에 게시하지 않는다.

> 이 글은 Docker·NPM·Ghost 공식 문서와 mLog의 공개 포트 점검 기록을 바탕으로 만든 전환 가이드다. 현재 운영 Compose의 실제 네트워크 멤버십과 버전은 이 초안 작성 중 확인하지 못했다. 발행 전 실제 파일·서비스명·백업·검증 결과와 대조해야 하며, 적용하지 않은 구성을 “운영 적용 완료”라고 표현하지 않는다.

## 3줄 요약

- NPM과 Ghost는 하나의 공유 프록시 네트워크에서 `ghost-web:2368`로 통신하고, Ghost와 MySQL은 별도 내부 네트워크에서 `db:3306`으로 통신한다.
- `ports`는 호스트에 포트를 게시한다. 같은 Docker 네트워크의 컨테이너끼리 통신하는 데는 Ghost 2368이나 MySQL 3306의 호스트 게시가 필요하지 않다.
- 네트워크 변경은 컨테이너 재생성을 일으킬 수 있다. 백업과 현재 구성 기록을 남기고 `config`, 네트워크 멤버십, 로그, 공개 HTTPS, 외부 포트를 순서대로 검증한다.

## 먼저 구분할 것: `ports`, `expose`, `external`, `internal`

네 가지 설정은 이름이 비슷해도 역할이 다르다.

| 설정             | 의미                             | 보안상 해석                                |
| -------------- | ------------------------------ | ------------------------------------- |
| ports          | 컨테이너 포트를 호스트 주소·포트에 게시         | 외부 접근 경로가 생길 수 있으므로 필요한 서비스에만 사용      |
| expose         | 컨테이너가 사용하는 내부 포트를 문서화          | 호스트에 게시하지 않지만, 그 자체가 네트워크 접근 통제는 아님   |
| external: true | Compose 밖에서 만든 기존 네트워크를 사용     | 여러 Compose 프로젝트를 연결할 때 유용하며 미리 존재해야 함 |
| internal: true | 외부 연결용 기본 게이트웨이가 없는 격리 네트워크 생성 | DB처럼 외부 연결이 필요 없는 서비스에 적합             |

Docker 공식 문서에 따르면 브리지 네트워크의 미게시 포트는 기본적으로 호스트 밖에서 접근할 수 없다. `-p` 또는 Compose의 `ports`를 사용하면 호스트 주소에 포트가 매핑된다.

또한 `expose`는 방화벽 규칙이 아니다. 같은 네트워크의 컨테이너는 이미지가 이미 노출 정보를 선언했다면 Compose에 `expose`가 없어도 해당 컨테이너 포트로 통신할 수 있다. 실제 경계는 **어떤 네트워크에 연결했는지**와 **호스트에 포트를 게시했는지**로 판단해야 한다.

## 목표 토폴로지

구성 요소별 연결은 다음 표처럼 단순하게 유지한다.

| 서비스                 | 공용 프록시망 edge\_proxy | 내부 데이터망 ghost\_backend | 호스트 게시 포트                  |
| ------------------- | ------------------- | ---------------------- | -------------------------- |
| Nginx Proxy Manager | 연결                  | 연결하지 않음                | 80·443, 관리 포트는 기존 보호 정책 유지 |
| Ghost               | 연결                  | 연결                     | 없음                         |
| MySQL               | 연결하지 않음             | 연결                     | 없음                         |

핵심은 Ghost가 두 네트워크 사이의 애플리케이션 경계가 되고, NPM과 MySQL이 서로 같은 네트워크를 공유하지 않는 것이다.

NPM의 관리자 포트와 NPM 자체 데이터베이스는 별도 문제다. 이 글의 예시는 기존 NPM 스택의 기본 네트워크를 없애지 않고, NPM 애플리케이션 컨테이너에 프록시용 공유 네트워크만 하나 더 연결한다.

Ghost 공식 Docker 설치 도구는 현재 Caddy와 MySQL을 포함하는 별도 구성을 안내한다. 아래 예시는 이미 Nginx Proxy Manager로 TLS와 프록시를 운영하는 사용자 정의 Compose 스택을 위한 것이며, 공식 설치 도구의 생성 파일을 그대로 대체하는 예시가 아니다.

## 변경 전: 실제 구성을 읽기 전용으로 기록한다

먼저 각 Compose 디렉터리에서 최종 적용 모델과 서비스·네트워크 이름을 확인한다.

```bash
docker compose config -q
docker compose config --services
docker compose config --networks
docker network ls
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'

```

Docker 공식 문서에서 `docker compose config`는 여러 Compose 파일과 환경변수를 합쳐 Docker Engine에 적용할 실제 모델을 렌더링한다. `-q`는 출력 없이 유효성을 검사한다.

특정 컨테이너의 네트워크 이름만 제한해서 본다.

```bash
docker inspect \
  --format '{{json .NetworkSettings.Networks}}' \
  '<CONTAINER_NAME>'

```

전체 `docker inspect`와 `docker compose config` 출력에는 환경변수, 내부 IP, 마운트 경로가 포함될 수 있다. 공개 글이나 질문에 원문 전체를 붙이지 않는다.

변경 전에는 다음을 별도로 보존한다.

- NPM·Ghost Compose 파일과 `.env`
- Ghost content 디렉터리 백업
- MySQL 논리 백업과 실제 복원 가능 여부
- 현재 NPM Proxy Host의 Forward Hostname·Port
- 현재 `docker ps` 포트 매핑과 네트워크 멤버십 요약

백업은 파일 존재만으로 성공으로 보지 않는다. 복원 검증 원칙은 [Ghost·MySQL 백업 복원 검증과 Docker 실패 포렌식](https://mlog.me/server-security-check-backup-restore-docker-forensics/)에 정리돼 있다.

## 1단계: NPM과 Ghost가 공유할 외부 네트워크를 준비한다

NPM과 Ghost가 서로 다른 Compose 프로젝트라면 한 프로젝트의 기본 네트워크 이름을 다른 프로젝트에 하드코딩하기보다, 수명주기를 Compose 밖에서 관리하는 공유 네트워크를 명시적으로 만든다.

```bash
export EDGE_NETWORK_NAME='<EXISTING_OR_NEW_EDGE_NETWORK>'

docker network inspect "$EDGE_NETWORK_NAME" >/dev/null 2>&1 || \
  docker network create "$EDGE_NETWORK_NAME"

```

`<EXISTING_OR_NEW_EDGE_NETWORK>`는 실제 이름으로 바꾼다. 이미 NPM이 사용하는 사용자 정의 공유 네트워크가 있다면 중복 생성하지 않고 그 네트워크가 맞는지 먼저 확인한다.

Compose에서 기존 네트워크를 참조하는 현재 문법은 다음과 같다.

```yaml
networks:
  edge_proxy:
    external: true
    name: "${EDGE_NETWORK_NAME}"

```

과거의 `external.name` 중첩 문법은 더 이상 권장되지 않는다. 문법 경고와 안전한 변경 방식은 [Docker Compose version·external.name 경고 해결법](https://mlog.me/docker-compose-version-obsolete-external-name-deprecated/)에서 확인할 수 있다.

외부 네트워크는 `docker compose up` 전에 존재해야 한다. 존재하지 않으면 Compose는 자동 생성하지 않고 `Network not found` 오류로 중단한다.

## 2단계: NPM 스택에 공유 프록시망만 추가한다

NPM의 기존 기본 네트워크와 자체 DB 연결은 유지한다. 아래 예시는 NPM 애플리케이션 서비스 이름이 `app`인 경우다. 실제 서비스명은 `docker compose config --services` 결과에 맞춘다.

```yaml
services:
  app:
    image: jc21/nginx-proxy-manager:<CURRENT_PINNED_TAG>
    networks:
      - default
      - edge_proxy

networks:
  edge_proxy:
    external: true
    name: "${EDGE_NETWORK_NAME}"

```

`<CURRENT_PINNED_TAG>`를 그대로 실행하지 않는다. 현재 운영 중인 고정 이미지 태그를 유지하고, 네트워크 변경과 버전 업데이트를 한 번에 섞지 않는다.

NPM 공식 문서는 같은 Docker 호스트의 업스트림 서비스와 사용자 정의 네트워크를 공유하면 업스트림 포트를 호스트 전체에 게시할 필요가 없다고 설명한다. 다만 공유 네트워크에 연결된 서비스끼리는 서로 접근 가능한 범위가 넓어진다. DB, Docker API, Portainer Agent 같은 관리·데이터 서비스를 이 네트워크에 무분별하게 추가하지 않는다.

## 3단계: Ghost는 프록시망과 내부망에 모두 연결한다

Ghost는 NPM의 요청을 받아야 하고 MySQL에도 연결해야 하므로 두 네트워크에 참여한다.

```yaml
services:
  ghost:
    image: ghost:<CURRENT_PINNED_TAG>
    environment:
      database__client: mysql
      database__connection__host: db
      database__connection__port: 3306
    networks:
      edge_proxy:
        aliases:
          - ghost-web
      ghost_backend:
    expose:
      - "2368"
    # ports는 두지 않음

  db:
    image: mysql:<CURRENT_PINNED_TAG>
    networks:
      - ghost_backend
    expose:
      - "3306"
    # ports는 두지 않음

networks:
  edge_proxy:
    external: true
    name: "${EDGE_NETWORK_NAME}"

  ghost_backend:
    internal: true

```

위 `expose`는 의도를 읽기 쉽게 보여 주기 위한 선택 항목이다. Ghost와 MySQL 이미지가 이미 컨테이너 포트를 선언했다면 같은 네트워크의 통신을 위해 반드시 필요하지 않다.

`ghost-web` 별칭은 공유 프록시망에서 NPM이 사용할 고유 이름이다. NPM 공식 문서는 하나의 공유 네트워크에서 서비스명 중복을 피하라고 안내한다. 여러 프로젝트가 모두 `app`, `web` 같은 이름을 사용한다면 충돌할 수 있으므로 사이트별 고유 별칭을 정하는 편이 안전하다.

MySQL은 `ghost_backend`에만 연결한다. NPM은 이 네트워크에 참여하지 않으므로 NPM에서 MySQL로 직접 가는 경로를 만들지 않는다.

## 4단계: `internal: true`의 부작용을 먼저 이해한다

Docker 공식 문서에서 `internal: true` 네트워크는 호스트 인터페이스와 연결되지 않고 외부 연결용 기본 게이트웨이가 없다. DB처럼 외부 인터넷이 필요 없는 서비스에는 적합하지만 다음 기능은 별도 검토가 필요하다.

- 외부 DB 복제 대상과 직접 통신
- 원격 백업 저장소로 직접 업로드
- 외부 DNS·HTTP API 호출
- 패키지 다운로드나 외부 플러그인 조회

MySQL이 외부 통신을 실제로 사용한다면 무조건 `internal: true`로 바꾸지 않는다. 필요한 목적지와 경로를 먼저 확인한다.

Ghost는 `ghost_backend` 외에 외부 연결이 가능한 `edge_proxy`에도 연결돼 있으므로 그 네트워크를 통해 SMTP, 웹훅, 외부 API에 접근할 수 있다. 그러나 `edge_proxy`까지 `internal: true`로 만들면 이메일·로그인·웹훅·업데이트 확인이 실패할 수 있다. 프록시 공유망과 DB 내부망의 역할을 섞지 않는다.

## 5단계: NPM의 Forward Hostname을 컨테이너 별칭으로 바꾼다

NPM과 Ghost가 `edge_proxy`에 모두 연결된 것을 확인한 뒤 Proxy Host의 업스트림을 다음처럼 설정한다.

| 항목                    | 예시              |
| --------------------- | --------------- |
| Scheme                | http            |
| Forward Hostname / IP | ghost-web       |
| Forward Port          | 2368            |
| 공개 도메인                | 기존 Ghost 도메인 유지 |

TLS 종료가 NPM에서 이뤄지는 구성이라면 NPM에서 Ghost까지는 Docker 네트워크의 HTTP 2368을 사용할 수 있다. Ghost의 공개 `url` 설정은 실제 외부 주소인 `https://mlog.me`와 일치해야 한다.

NPM UI 값을 먼저 바꾸고 Ghost를 네트워크에 연결하지 않으면 `502 Bad Gateway`가 발생할 수 있다. 전환 순서는 다음처럼 잡는다.

1. 공유 네트워크를 준비한다.
2. NPM과 Ghost를 공유 네트워크에 연결한다.
3. `ghost-web` 별칭이 보이는지 확인한다.
4. NPM Forward Hostname을 바꾼다.
5. 공개 HTTPS 응답을 확인한다.
6. 마지막에 Ghost 2368의 호스트 `ports`를 제거한다.

## 6단계: MySQL 3306의 호스트 게시를 제거한다

Ghost는 `ghost_backend`에서 `db:3306`으로 연결한다. 내부 통신에는 다음 설정이 필요하지 않다.

```yaml
ports:
  - "3306:3306"

```

Docker 공식 문서에서 호스트 IP를 생략한 게시 포트는 기본적으로 호스트의 모든 주소에 바인딩될 수 있다. 또한 Docker가 만든 NAT·포워딩 규칙은 UFW의 일반 입력 규칙보다 앞에서 처리될 수 있으므로 `ufw deny 3306`만 보고 비공개라고 단정하면 안 된다.

먼저 Compose에서 `ports`를 제거하고, Docker 게시 상태와 외부 관찰을 함께 확인한다. 외부 포트 점검의 판정 기준은 [외부 노출 포트와 방화벽 상태 확인](https://mlog.me/server-security-check-external-ports-firewall/)에 정리돼 있다.

## 7단계: 구성 검증 후 재생성 범위를 확인한다

변경한 Compose 파일을 적용하기 전에 문법과 최종 네트워크 목록을 확인한다.

```bash
docker compose config -q
docker compose config --services
docker compose config --networks

```

네트워크 설정이 바뀌면 `docker compose up`이 해당 서비스 컨테이너를 중지하고 재생성할 수 있다. Docker 공식 문서는 이미지나 서비스 구성이 바뀌면 기존 컨테이너를 재생성하되 마운트된 볼륨은 보존한다고 설명한다.

적용 명령은 실제 유지보수 창과 Compose 버전에 맞춰 선택한다.

```bash
docker compose up -d --wait

```

`--wait`는 서비스가 `running` 또는 `healthy` 상태가 될 때까지 기다린다. 유효한 healthcheck가 없는 서비스는 애플리케이션 요청 처리 준비까지 보장하지 않는다.

네트워크 변경과 이미지 업그레이드, 비밀번호 교체, 볼륨 마이그레이션을 한 번에 수행하지 않는다. 실패했을 때 어느 변경이 원인인지 분리하기 어렵다.

## 8단계: 네트워크 멤버십을 표로 검증한다

두 네트워크의 컨테이너 목록을 확인한다.

```bash
docker network inspect "$EDGE_NETWORK_NAME"
docker network inspect '<GHOST_BACKEND_NETWORK>'

```

전체 JSON에는 내부 IP와 컨테이너 이름이 있으므로 공개할 때는 다음 판정만 남긴다.

| 검증 항목             | 기대 결과                   |
| ----------------- | ----------------------- |
| edge\_proxy 멤버    | NPM과 Ghost 존재, MySQL 없음 |
| ghost\_backend 멤버 | Ghost와 MySQL 존재, NPM 없음 |
| NPM 업스트림          | ghost-web:2368 응답       |
| Ghost DB DNS      | db가 내부 IP로 해석           |
| MySQL 게시 포트       | 호스트 매핑 없음               |
| Ghost 게시 포트       | 호스트 매핑 없음               |

Ghost 컨테이너 안에서 DB 서비스명 DNS를 읽기 전용으로 확인한다.

```bash
docker compose exec ghost node -e \
  "require('node:dns').lookup('db', (err, address) => console.log(err || address))"

```

`db`가 내부 IP로 해석돼도 MySQL 인증과 스키마 권한까지 성공했다는 뜻은 아니다. Ghost 로그와 실제 페이지 응답을 추가로 확인한다.

## 9단계: 게시 포트와 공개 HTTPS를 확인한다

Compose 서비스 상태와 게시 포트를 확인한다.

```bash
docker compose ps

docker port "$(docker compose ps -q ghost)"
docker port "$(docker compose ps -q db)"

```

Ghost와 DB에 호스트 게시 포트가 없다면 `docker port`에는 매핑이 출력되지 않아야 한다.

공개 경로는 NPM을 거쳐 정상 응답해야 한다.

```bash
curl -sS -o /dev/null \
  -w 'http_code=%{http_code}\n' \
  https://mlog.me/

```

Ghost 최근 로그에서 새 DB 연결 오류와 프록시 오류를 확인한다.

```bash
docker compose logs --since=10m ghost

```

자신이 관리하는 서버를 외부 네트워크에서 점검할 권한이 있을 때만 필요한 포트를 제한해서 확인한다.

```bash
nmap -Pn -sT -p 80,443,2368,3306 '<PUBLIC_DOMAIN_OR_IP>'

```

기대 결과는 80·443의 정책상 상태가 유지되고 2368·3306이 인터넷에서 직접 `open`으로 보이지 않는 것이다. `filtered`는 닫힘이 아니라 스캔 지점에서 판별할 수 없다는 뜻이므로 내부 Docker 게시 상태와 함께 해석한다.

## 실패 증상별 원인

| 증상                         | 가능성이 높은 원인                          | 먼저 확인할 곳                       |
| -------------------------- | ----------------------------------- | ------------------------------ |
| NPM 502 Bad Gateway        | NPM과 Ghost가 공유망에 없음, 별칭·포트 오류       | edge\_proxy 멤버, ghost-web:2368 |
| ENOTFOUND ghost-web        | 외부 네트워크 누락 또는 별칭 충돌                 | NPM·Ghost 네트워크와 별칭             |
| Ghost ENOTFOUND db         | Ghost와 MySQL이 내부망을 공유하지 않음          | ghost\_backend 멤버, DB 서비스명     |
| Ghost ECONNREFUSED db:3306 | MySQL 미기동 또는 준비 전                   | DB 상태, 로그, healthcheck         |
| 이메일·웹훅 실패                  | Ghost가 외부 연결 가능한 네트워크를 잃음           | Ghost 네트워크 목록, DNS·출구 경로       |
| 원격 백업 실패                   | DB가 internal 네트워크에만 있어 외부 목적지 접근 불가 | 백업 실행 주체와 전송 경로                |

`ENOTFOUND db`의 상세한 비파괴 진단 순서는 [Ghost getaddrinfo ENOTFOUND db 해결](https://mlog.me/ghost-getaddrinfo-enotfound-db-docker-compose-mysql/)을 참고한다.

## 롤백 순서

전환이 실패하면 데이터 볼륨을 건드리지 않고 네트워크와 프록시 설정만 원래 상태로 되돌린다.

1. 변경 전 Compose 파일과 `.env`를 복구한다.
2. NPM Forward Hostname·Port를 변경 전 값으로 되돌린다.
3. `docker compose config -q`로 복구 구성을 검증한다.
4. `docker compose up -d --wait`로 필요한 컨테이너를 재생성한다.
5. Ghost 로그와 공개 HTTPS를 다시 확인한다.
6. 공유 외부 네트워크는 연결된 컨테이너가 없는 것을 확인하기 전 삭제하지 않는다.

다음 명령은 네트워크 문제 해결용 롤백이 아니다.

```bash
# 실행 금지: 데이터베이스 볼륨이 삭제될 수 있음
# docker compose down -v

# 실행 금지: MySQL 데이터가 저장된 볼륨일 수 있음
# docker volume rm <DATABASE_VOLUME>

```

네트워크 멤버십이나 NPM 업스트림 오류를 해결하는 데 DB 볼륨 삭제는 필요하지 않다.

## 보안상 남는 한계

네트워크를 둘로 나눈다고 전체 보안이 완성되는 것은 아니다.

- `edge_proxy`의 모든 멤버는 같은 네트워크에서 서로의 노출 포트에 접근할 수 있다.
- NPM 관리자 UI와 Portainer 같은 관리면에는 별도 인증·접근 제어가 필요하다.
- DB 계정은 Ghost 데이터베이스에 필요한 최소 권한만 가져야 한다.
- Ghost·MySQL 이미지와 Docker Engine은 지원되는 버전으로 유지해야 한다.
- 백업 파일과 `.env` 권한, 비밀 교체, 로그 민감정보 제거가 별도로 필요하다.
- 호스트·클라우드 방화벽과 IPv6 경로도 함께 확인해야 한다.

NPM·Authelia의 관리면 접근 정책은 [Nginx Proxy Manager와 Authelia 접근 제어](https://mlog.me/server-security-check-npm-authelia-access-control/)에서 별도로 다룬다.

## 재발 방지 체크리스트

- \[ \] NPM과 Ghost만 공유 프록시망에 연결했는가
- \[ \] Ghost와 MySQL만 내부 데이터망을 공유하는가
- \[ \] MySQL이 프록시망에 연결돼 있지 않은가
- \[ \] Ghost 2368과 MySQL 3306의 `ports`를 제거했는가
- \[ \] NPM Forward Hostname이 고유한 네트워크 별칭인가
- \[ \] `external` 네트워크가 배포 전에 존재하는가
- \[ \] `internal: true`로 인해 필요한 외부 백업·복제 경로가 끊기지 않는가
- \[ \] `docker compose config -q`와 `--networks`를 확인했는가
- \[ \] 변경 후 Ghost의 `db` DNS, 로그, 공개 HTTPS를 검증했는가
- \[ \] 외부 스캔과 Docker 게시 포트를 함께 확인했는가
- \[ \] `down -v`와 DB 볼륨 삭제를 실행하지 않았는가

## 마무리

Nginx Proxy Manager, Ghost, MySQL을 한 네트워크에 모두 넣거나 모든 포트를 호스트에 게시할 필요는 없다.

가장 단순한 경계는 다음 세 줄이다.

1. NPM과 Ghost는 공유 프록시망에서 `ghost-web:2368`로 통신한다.
2. Ghost와 MySQL은 내부 데이터망에서 `db:3306`으로 통신한다.
3. MySQL은 프록시망과 호스트 게시 포트를 갖지 않는다.

이 구조는 연결을 끊기 위한 것이 아니라 **필요한 통신만 남기는 것**이다. 전환 전 실제 Compose 모델과 백업을 보존하고, 변경 후 네트워크 멤버십·DNS·로그·HTTPS·외부 포트를 차례대로 확인해야 한다.

## 공식 문서

- [Docker Compose 네트워킹](https://docs.docker.com/compose/how-tos/networking/?ref=mlog.me)
- [Compose 네트워크 속성: external·internal·name](https://docs.docker.com/reference/compose-file/networks/?ref=mlog.me)
- [Docker 포트 게시와 매핑](https://docs.docker.com/engine/network/port-publishing/?ref=mlog.me)
- [Compose 서비스의 expose](https://docs.docker.com/reference/compose-file/services/?ref=mlog.me#expose)
- [docker compose config](https://docs.docker.com/reference/cli/docker/compose/config/?ref=mlog.me)
- [docker compose up과 --wait·재생성](https://docs.docker.com/reference/cli/docker/compose/up/?ref=mlog.me)
- [docker network inspect](https://docs.docker.com/reference/cli/docker/network/inspect/?ref=mlog.me)
- [Docker와 UFW·패킷 필터링](https://docs.docker.com/engine/network/packet-filtering-firewalls/?ref=mlog.me)
- [Nginx Proxy Manager 고급 구성: Docker network](https://nginxproxymanager.com/advanced-config/?ref=mlog.me)
- [Ghost 구성 문서](https://docs.ghost.org/config?ref=mlog.me)
- [Ghost Docker 설치 안내](https://docs.ghost.org/install/docker?ref=mlog.me)