워드프레스 하나 운영하자고 맥미니 안에 Ubuntu 가상머신까지 만드는 건 솔직히 가장 쉬운 길은 아니다. 글만 빨리 쓰고 싶다면 관리형 호스팅을 신청하는 편이 훨씬 간단하다.
이미 쓰던 맥미니가 있어 새 장비 대신 그 안에 Ubuntu VM을 하나 두었다. 조건은 두 가지였다.
기존 macOS 작업과 WordPress 서버를 섞지 않을 것, 공유기 포트는 열지 않을 것.
그렇게 올린 ttakdap.com은 WordPress와 MariaDB를 VM 안에서 돌리고, 외부 연결은 Cloudflare Tunnel이 맡고 있다.
WordPress 설치보다 시간을 더 쓴 건 공개 범위를 정하고, 관리자 입구를 따로 잠그고, 고장 났을 때 되살릴 길을 만드는 일이었다.
현재 판정
맥미니로 24시간 운영을 시작할 준비는 갖췄다. 하지만 장기간의 안정성과 정전 후 완전한 무인 복구까지 입증한 것은 아니다.

그림 1. 방문자는 Cloudflare Tunnel을 거쳐 WordPress에 접속하고, MariaDB는 Docker 내부 네트워크에만 둔 실제 구성. 백업은 VM 안의 복구 세트와 암호화된 R2 사본으로 나눴다.
직접 따라 하는 설치 안내
이 글은 구성 이유와 운영하면서 확인한 장단점을 정리한 글이다. 처음부터 같은 서버를 만들고 싶다면 아래 설치 시리즈를 순서대로 보면 된다. 각 편은 앞 단계의 성공 여부를 확인한 뒤 다음 단계로 넘어가도록 나눴다.
- Parallels에 Ubuntu Server VM 만들기 — ISO 선택부터 SSH 키 접속까지
- Ubuntu에 Docker로 WordPress·MariaDB 설치하기 — 외부 공개 전 내부 구동까지
- Cloudflare Tunnel로 공개하고 관리자 경로 보호하기
- WP-Cron을 systemd로 바꾸고 Cloudflare R2에 백업하기 — 복구 파일 재다운로드와 무결성 검사까지
한 글로 모두 합치지 않은 이유는 설치 위치와 오류 범위를 분명히 하기 위해서다. 1편에서는 Ubuntu와 SSH까지만, 2편에서는 외부에 공개하지 않은 WordPress 내부 구동까지 확인한다.
이번 구성
호스트는 M4 Pro 12코어와 메모리 64GB를 갖춘 맥미니다. 이 사양이 WordPress 운영에 꼭 필요하다는 뜻은 아니다. 이번 글은 성능 한계를 재는 벤치마크가 아니라, 가지고 있던 맥미니에 서버 역할을 분리해 올린 기록이다.
| 구분 | 실제 구성 |
|---|---|
| 가상머신 | Parallels의 Ubuntu Server 24.04 LTS ARM64 |
| VM 자원 | 4 vCPU, 메모리 8GB, 64GB 확장형 디스크, NAT |
| 웹 | Docker Compose의 WordPress · PHP · Apache |
| 데이터베이스 | 별도 MariaDB 컨테이너 |
| 외부 연결 | Cloudflare DNS · TLS · Tunnel |
| 관리자 보호 | /wp-login.php, /wp-admin/*에 Cloudflare Access |
| 예약 작업 | WordPress Cron 5분마다, 로컬 백업 6시간마다 |
| 외부 백업 | 하루 최대 한 번, 암호화 후 Cloudflare R2로 전송 |
컨테이너 이미지는 계속 바뀌는 latest 대신 구축할 때 확인한 버전으로 고정했다.
업데이트는 새 이미지를 무조건 받게 두지 않고, 백업을 먼저 만든 다음 재기동과 상태 검사를 거치도록 순서를 정했다.
macOS에 바로 설치하지 않고 VM으로 나눈 이유
처음에는 macOS에서 Docker를 바로 실행하는 방법도 생각했다. 다만 이 맥에는 이미 다른 컨테이너가 돌아가고 있었다. 사이트 서버를 수정하다 기존 작업까지 건드리는 상황은 피하고 싶었다.
그래서 WordPress 운영 환경을 Parallels의 Ubuntu VM 하나에 몰아넣었다. VM으로 나누니 macOS 쪽 작업과 WordPress 서버가 얽히지 않았다. Linux 서버용 문서와 명령도 거의 그대로 쓸 수 있었다. 이상이 생겼을 때는 Mac → VM → 컨테이너 순으로 확인 범위를 좁힐 수 있다.
대신 부팅 경로가 길어졌다. 맥미니, macOS, Parallels, Ubuntu, Docker가 모두 올라와야 사이트가 열린다. 컨테이너에 자동 재시작 옵션을 넣었다고 해서 정전 후 모든 단계가 저절로 해결되는 것은 아니다.
이 점은 나중에 다시 설명하겠지만, 맥미니를 24시간 서버로 볼 때 가장 큰 약점이기도 하다.
한 번에 공개하지 않고 안쪽부터 확인했다
처음부터 WordPress와 데이터베이스, Tunnel을 한꺼번에 인터넷에 연결하지는 않았다. 먼저 Ubuntu VM 안에서 MariaDB와 WordPress만 올리고, 데이터베이스가 준비됐는지와 WordPress 컨테이너의 80번 포트가 내부 연결을 받는지를 확인했다. 이 단계에서는 Tunnel 토큰을 넣지 않아 사이트가 아직 외부 주소로 연결되지 않았다.
내부 구동을 확인한 뒤에야 cloudflared를 추가했다. WordPress 초기 설정이 끝나기 전에는 사이트 전체를 소유자만 통과할 수 있도록 임시로 잠갔고, 설정을 마친 다음 공개 글과 홈페이지의 잠금을 풀었다. 지금은 /wp-login.php와 /wp-admin/*만 Cloudflare Access로 보호하고, 공개 페이지는 인증 없이 열린다.
순서를 나누니 문제가 생겼을 때 확인할 범위도 줄었다. VM 안에서도 열리지 않으면 WordPress나 MariaDB 쪽을 보고, 내부에서는 열리는데 주소로 접속되지 않으면 Tunnel·DNS·Access 설정을 확인하면 된다.
실제로 해보니 홈서버를 공개하는 일은 ‘컨테이너를 실행했다’에서 끝나지 않았다. 각 단계의 성공 조건을 확인하면서 접속 범위를 조금씩 넓히는 과정에 가까웠다.
MariaDB·WordPress 내부 기동 → 내부 상태 검사 → Tunnel 연결 → 초기 설정 동안 임시 전체 보호 → 공개 페이지 개방·관리자 경로만 보호

그림 2. 실제 배포 순서를 기준으로, 외부 접속 범위를 한 단계씩 넓힌 흐름을 재구성했다.
공유기 80·443 포트는 열지 않았다
홈서버를 인터넷에 공개할 때 가장 먼저 떠올린 것은 포트포워딩이었다. 이번 구성에서는 공유기에 웹용 포트포워딩을 만들지 않았고, WordPress용 Docker Compose에도 호스트 포트를 공개하는 ports 항목을 넣지 않았다.
대신 VM 안의 cloudflared 컨테이너가 Cloudflare 쪽으로 먼저 연결한다. 방문자는 Cloudflare를 거쳐 이 터널로 WordPress에 들어온다.
집 밖에서 VM으로 직접 들어오는 길을 새로 만드는 대신, 서버가 외부로 내보내는 연결을 이용한 셈이다.
MariaDB는 더 안쪽에 뒀다. WordPress와 DB만 연결되는 Docker 내부 전용 네트워크를 만들고, DB 포트는 외부는 물론 호스트에도 공개하지 않았다.
다만 “Tunnel을 썼으니 안전하다”고 말할 수는 없다. WordPress와 플러그인을 업데이트해야 하고, 계정 보안과 백업도 따로 챙겨야 한다. 포트를 공개하지 않은 것은 공격면을 줄인 것이지 보안 작업을 끝낸 것이 아니다.
공개 사이트와 관리자 화면을 따로 취급했다
관리자 로그인 앞에는 Cloudflare Access를 한 겹 더 뒀다. 보호 대상은 WordPress 로그인 화면과 관리자 경로다.
/wp-login.php/wp-admin/*
사이트 전체를 Access로 잠그지는 않았다. 그렇게 하면 일반 방문자뿐 아니라 검색엔진과 광고 크롤러도 인증 화면으로 보내질 수 있기 때문이다. 공개 글과 홈페이지는 로그인 없이 열고, 관리자 화면만 별도로 보호했다.
admin-ajax.php는 이름과 달리 공개 테마나 플러그인에서도 사용할 수 있다. 다만 현재 확인에서는 이 경로도 관리자 규칙과 함께 Access 보호를 받고 있다. 공개 화면의 실제 기능이 이 경로를 요구하는 것이 확인되면 그때 정확한 경로만 조건부로 예외 처리할 계획이다. 필요성을 확인하지 않고 먼저 열지는 않았다.
DB 비밀번호와 Tunnel 토큰도 Compose 파일이나 일반 환경변수에 직접 적지 않았다. 권한을 제한한 파일 기반 secret으로 전달했고, Tunnel 컨테이너에는 읽기 전용 파일시스템과 권한 축소 설정을 적용했다. 완벽한 방어라고 부를 수는 없지만, 실수로 비밀값이 로그나 설정 화면에 노출될 가능성을 줄이는 데는 의미가 있다.
running 세 글자만 믿지 않도록 했다
Docker에서 컨테이너가 running으로 보이더라도 WordPress가 실제로 DB에 로그인하지 못하거나 Apache의 80번 포트가 연결을 받지 못할 수 있다.
그래서 상태 확인을 몇 단계로 나눴다.
MariaDB가 연결을 받을 준비가 됐는지, WordPress가 secret 파일을 읽는지, 그 비밀번호로 DB 인증이 되는지, WordPress 컨테이너 안에서 Apache의 80번 포트에 TCP 연결이 되는지 차례로 검사한다. 이 기본 검사는 HTTP 본문까지 확인하는 검사가 아니다. Tunnel도 잠깐 실행됐다가 종료되는 상태를 놓치지 않도록 연속된 확인을 넣었다.
업데이트 순서도 같은 이유로 정했다.
새 복구 세트 생성 → 이미지 업데이트 → 컨테이너 재기동 → 내부 상태 검사 → 외부 접속 확인
초안 작성일에 Ubuntu의 예약 작업 상태를 다시 보니 WordPress Cron 타이머와 6시간 주기 백업 타이머가 모두 활성 상태였고, 다음 실행 시각도 정상적으로 잡혀 있었다.

그림 3. 2026년 9월 1일 읽기 전용으로 다시 확인한 결과를 사용자명·호스트명·내부 주소 없이 재구성했다. 특정 시점의 상태이며 장기 가동률을 뜻하지는 않는다.
백업은 ‘만들었다’보다 ‘다시 읽혔다’가 중요했다
VM 안에만 백업을 두면 가상 디스크나 맥미니 저장장치에 문제가 생길 때 원본과 백업을 함께 잃을 수 있다. 그래서 VM 안의 복구 세트와 R2 사본을 나눴다.
현재는 6시간마다 같은 시각표를 가진 복구 세트 하나를 만든다. 여기에는 MariaDB 논리 덤프, WordPress 전체 파일, 비밀값을 제외한 배포 설정, 실제 Compose 구성, 컨테이너 이미지 목록, SHA-256 검증값이 들어간다. 작성 중인 백업은 임시 위치에 두고, 모든 파일의 검증이 끝나야 완성된 복구 세트로 취급한다.
로컬 복구 세트의 기본 보존 기간은 14일이다. 6시간 간격이면 설정상 약 56세트가 남는다. 사진과 영상이 늘면 전체 파일 압축본도 커지기 때문에, 이 숫자만 믿지 않고 VM 디스크 사용량을 함께 봐야 한다.
하루에 최대 한 세트는 rclone으로 암호화해 Cloudflare R2로 보낸다. 구성할 때는 업로드 성공 메시지만 보고 끝내지 않았다. 같은 복구 세트를 다시 내려받아 SHA-256 검사가 통과하는 것까지 확인했다.
이번에 확인한 범위는 여기까지다. R2에서 다시 받은 파일의 SHA-256은 일치했지만, 빈 Ubuntu VM에서 사이트 전체를 복원하는 시험은 아직 하지 않았다.
그래서 24시간 서버라고 불러도 될까
사이트는 외부에서 열리고, WordPress·DB·Tunnel 컨테이너에는 재시작 정책이 들어가 있다.
WordPress Cron은 5분마다, 백업은 6시간마다 Ubuntu의 systemd 타이머가 실행한다.
서버가 잠깐 꺼져 예약 시간을 놓쳤을 때 다시 처리하도록 Persistent 설정도 켰다.
그런데 이 자동화는 Ubuntu VM이 이미 부팅된 다음 이야기다.
정전이 나면 맥미니가 다시 켜져야 하고, macOS 로그인과 Parallels 시작을 거쳐 Ubuntu VM이 올라와야 한다. 현재 구성에서는 사용자 로그인 전에도 Parallels와 Ubuntu VM이 시작되는지 아직 검증하지 않았다.
그래서 지금 “정전 뒤에도 사람 손 없이 자동 복구된다”고 쓰면 과장이다. 실제로 남은 시험은 다음과 같다.
- 전원을 끊었다가 넣었을 때 맥미니가 다시 켜지는가.
- 사용자 로그인 없이 Parallels와 Ubuntu VM이 시작되는가.
- Docker와 세 컨테이너가 올바른 순서로 정상화되는가.
- 놓친 Cron과 백업 작업이 처리되는가.
- 외부에서 사이트가 다시 열릴 때까지 몇 분이 걸리는가.
수개월간의 가동률, 실제 소비전력, 온도와 팬 소음, 동시 접속 성능도 아직 측정하지 않았다. 이런 수치를 다른 사람의 맥미니 사용기에서 가져와 채우기보다는, 스마트 플러그와 모니터링 기록을 쌓은 뒤 후속 글에 추가할 생각이다.
지금 내린 결론
이미 쓰던 맥미니가 있고 서버 운영 자체를 배워 보고 싶다면 해볼 만했다. 반대로 글 쓰는 데만 집중하고 싶거나, 장애 뒤 정해진 시간 안에 복구돼야 한다면 관리형 호스팅이 편하다. 이 구성에서는 문제가 생길 때 macOS부터 Cloudflare까지 직접 확인해야 한다.
지금까지 끝낸 것은 사이트 공개, 관리자 경로 보호, 로컬·R2 백업 파일 검증까지다. 정전 뒤 무인 기동과 빈 VM 전체 복구는 아직 남았다. 두 시험을 마친 뒤에야 이 서버를 24시간 운영용이라고 마음 놓고 부를 수 있을 것 같다.

그림 4. 2026년 9월 1일 관리자 표시를 제외하고 다시 캡처한 딱답 공개 홈페이지. 현재 맥미니의 Ubuntu VM에서 서비스 중이다.
