Ubuntu 24.04에서 Gitea만을 위한 OS 사용자 gitea-svc를 만들고, 이 사용자가 소유하는 별도의 Rootless Docker daemon에서 Gitea를 실행하는 방법을 정리합니다.
PostgreSQL은 Gitea와 같은 Docker daemon에 두지 않습니다. 별도의 postgres-svc Rootless Docker daemon에서 실행하고, Gitea container는 Ubuntu host의 private/internal IP에 공개된 PostgreSQL TCP endpoint로 접속합니다.
Ubuntu 24.04
│
├─ postgres-svc
│ └─ Rootless Docker daemon A
│ └─ PostgreSQL 18
│ └─ HOST_PRIVATE_IP:5432
│
└─ gitea-svc
└─ Rootless Docker daemon B
└─ Gitea 1.27.3-rootless
│
└─ TCP -> HOST_PRIVATE_IP:5432 -> PostgreSQL daemon A이 구조에서는 postgres-svc와 gitea-svc가 서로 다른 Docker daemon과 Docker socket을 사용합니다. 따라서 같은 Docker bridge network나 Docker DNS alias를 공유하지 않으며, Gitea는 PostgreSQL container 이름이 아니라 host에 공개된 TCP endpoint로 접속해야 합니다.
이 문서는 PostgreSQL용 Rootless Docker daemon과 PostgreSQL 18 구성이 먼저 완료되어 있다는 전제로 작성합니다. PostgreSQL이
127.0.0.1:5432에만 publish되어 있다면 다른 Rootless Docker daemon의 container에서 접근할 수 없으므로, Gitea container가 접근할 수 있는 host private/internal IP에 PostgreSQL을 bind해야 합니다.
설치 전 확인
먼저 PostgreSQL 설치 문서에서 다음 값을 확인합니다.
POSTGRES_BIND_IP
POSTGRES_PORT
PostgreSQL database: gitea
PostgreSQL role: gitea
gitea_db_password예를 들어 PostgreSQL이 같은 Ubuntu host의 private IP 192.168.10.50에 publish되어 있다면 다음과 같은 구성이 됩니다.
POSTGRES_BIND_IP=192.168.10.50
POSTGRES_PORT=5432Gitea container는 다음 주소로 PostgreSQL에 접속합니다.
192.168.10.50:5432다음 주소는 사용하지 않습니다.
127.0.0.1
platform-postgres127.0.0.1은 Gitea container 자신을 의미합니다.
platform-postgres와 같은 container 이름은 동일한 Docker network 안에서는 Docker DNS로 해석할 수 있지만, 현재 구성처럼 PostgreSQL과 Gitea가 서로 다른 Rootless Docker daemon에 있으면 Docker network와 DNS namespace도 분리되어 있으므로 사용할 수 없습니다.
Docker CE와 Rootless Docker에 필요한 package가 PostgreSQL 구성 단계에서 이미 설치되어 있다면 다시 설치할 필요는 없습니다.
Gitea 전용 OS 사용자와 Rootless Docker daemon
Gitea 전용 OS 사용자 생성
root 또는 운영자 계정에서 gitea-svc 사용자를 생성합니다.
sudo useradd \
--create-home \
--home-dir /var/lib/gitea-svc \
--shell /bin/bash \
gitea-svc직접 password login을 사용하지 않을 계획이라면 password를 잠급니다.
sudo passwd -l gitea-svc
"gitea-svc 계정의 비밀번호 입력을 통한 직접 접속을 완전히 막아(Lock) 시스템 보안을 강화하는 명령어"입니다.
사용자가 logout한 뒤에도 user systemd service가 유지되도록 linger를 활성화합니다.
sudo loginctl enable-linger gitea-svc
"gitea-svc 계정이 로그아웃하거나 직접 접속해있지 않아도, 해당 계정으로 실행 중인 서비스(Rootless Docker 등)가 꺼지지 않고 서버 재부팅 시에도 자동으로 켜지도록 보장하는 설정"입니다.
사용자를 확인합니다.
id gitea-svcsubordinate UID/GID 확인
Rootless Docker는 user namespace를 사용하므로 /etc/subuid와 /etc/subgid에 충분한 subordinate UID/GID range가 있어야 합니다.
확인합니다.
grep '^gitea-svc:' /etc/subuid
grep '^gitea-svc:' /etc/subgid정상 예:
gitea-svc:231072:65536Docker 공식 Rootless mode 문서에서는 사용자에게 최소 65,536개의 subordinate UID와 GID가 할당되어 있어야 한다고 안내합니다.
값이 없다면 기존 postgres-svc 또는 다른 Rootless Docker 사용자와 겹치지 않는 별도 range를 할당합니다.
예를 들어 이미 사용 중인 range를 먼저 확인합니다.
cat /etc/subuid
cat /etc/subgid그 뒤 사용하지 않는 범위를 선택하여 설정합니다.
sudo usermod --add-subuids 296608-362143 gitea-svc
sudo usermod --add-subgids 296608-362143 gitea-svc다시 확인합니다.
grep '^gitea-svc:' /etc/subuid
grep '^gitea-svc:' /etc/subgid
subordinate UID/GID range는 서비스 사용자마다 서로 겹치지 않게 관리하는 것이 좋습니다. 여러 Rootless Docker daemon을 분리하는 구조에서는 각 daemon의 user namespace 범위를 명확히 분리해야 운영 시 권한 문제를 추적하기 쉽습니다.
Gitea 전용 Rootless Docker 설치
gitea-svc의 로그인 환경으로 들어갑니다.
sudo machinectl shell gitea-svc@machinectl 명령이 설치되어 있지 않은 최소 Ubuntu Server 환경이라면 systemd-container package가 필요할 수 있습니다.
sudo apt install -y systemd-container다시 접속합니다.
sudo machinectl shell gitea-svc@이후 명령은 gitea-svc shell에서 실행합니다.
export XDG_RUNTIME_DIR=/run/user/$(id -u)현재 사용자를 확인합니다.
whoami
id -u정상적으로 다음과 같이 나와야 합니다.
gitea-svcRootless Docker를 설치합니다.
dockerd-rootless-setuptool.sh install설치 후 user Docker service를 활성화합니다.
systemctl --user enable --now docker
--user : root(시스템 전체) 영역이 아니라 , "현재 로그인된 사용자 계정 전용 영역" 의 서비스를 관리하겠다는 의미입니다. (Rootless Docker 환경에서 사용)
이 명령어는 앞서 진행하셨던 설정들과 다음과 같이 연결됩니다.
- loginctl enable-linger gitea-svc 로 로그인 유지를 활성화함.
- gitea-svc 계정으로 접속하여 systemctl --user enable --now docker 실행.
- → 이제 gitea-svc 계정 소유의 Rootless Docker 데몬이 지금 바로 시작되며, 서버 재부팅 시에도 로그인 없이 자동으로 Docker가 실행됩니다.
상태를 확인합니다.
systemctl --user status docker
docker infodocker info의 Security Options 등에 rootless가 표시되는지 확인합니다.
Docker socket도 확인합니다.
ls -l "$XDG_RUNTIME_DIR/docker.sock"예:
/run/user/<gitea-svc UID>/docker.sock이 socket은 postgres-svc의 socket과 완전히 다른 파일입니다.
예를 들어 다음과 같이 분리됩니다.
/run/user/1001/docker.sock # postgres-svc
/run/user/1002/docker.sock # gitea-svcHost의 rootful Docker daemon 사용 여부
Rootless Docker를 사용하기 위해 host의 rootful Docker daemon을 반드시 중지해야 하는 것은 아닙니다.
Rootful Docker와 각 Rootless Docker는 서로 다른 daemon, data directory, socket을 사용하므로 같은 host에서 함께 운영할 수 있습니다.
다만 Docker 공식 Rootless 설치 도구는 system-wide Docker daemon이 실행 중인 경우 충돌 방지를 위해 설치를 중단하고 --force 사용을 안내할 수 있습니다.
이 경우 rootful Docker workload도 함께 사용해야 한다면 rootful daemon을 중지하지 않고 다음처럼 설치합니다.
dockerd-rootless-setuptool.sh install --force반대로 이 host에서 rootful Docker를 전혀 사용하지 않고 모든 workload를 Rootless Docker로만 운영할 계획이라면 선택적으로 rootful daemon을 비활성화할 수 있습니다.
sudo systemctl disable --now docker.service docker.socket이 명령은 Rootless Docker의 필수 설치 단계가 아닙니다.
기존 rootful Docker container나 Compose workload가 있다면 실행하지 않습니다.
여러 Docker daemon이 함께 존재하는 서버에서는 현재 shell이 어느 daemon을 바라보는지를 명확히 하는 것이 중요합니다.
gitea-svc shell에서는 다음 값을 사용할 수 있습니다.
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock확인합니다.
echo "$DOCKER_HOST"
docker infoGitea 구성 파일 만들기
gitea-svc용 작업 디렉터리를 만들고, 그 안에서 compose.yaml, .env, secrets를 구성합니다.
최종 디렉터리는 다음 형태가 됩니다.
/var/lib/gitea-svc/platform/gitea/
├─ compose.yaml
├─ .env
└─ secrets/
└─ gitea_db_password작업 디렉터리 생성
root 또는 운영자 계정에서 Gitea 작업 디렉터리와 secret 디렉터리를 만듭니다.
sudo install -d \
-o gitea-svc \
-g gitea-svc \
/var/lib/gitea-svc/platform/gitea
sudo install -d \
-m 0700 \
-o gitea-svc \
-g gitea-svc \
/var/lib/gitea-svc/platform/gitea/secrets확인합니다.
sudo ls -ld \
/var/lib/gitea-svc/platform/gitea \
/var/lib/gitea-svc/platform/gitea/secretsPostgreSQL password 준비
PostgreSQL 구성 단계에서 생성한 gitea_db_password와 동일한 값을 Gitea에서도 사용해야 합니다.
PostgreSQL 설치 과정에서 다음 secret file을 만들었다고 가정합니다.
/var/lib/postgres-svc/platform/postgresql/secrets/gitea_db_password이 파일을 Gitea 전용 secret 디렉터리로 복사합니다.
sudo install -m 0600 \
-o gitea-svc \
-g gitea-svc \
/var/lib/postgres-svc/platform/postgresql/secrets/gitea_db_password \
/var/lib/gitea-svc/platform/gitea/secrets/gitea_db_password확인합니다.
sudo ls -l /var/lib/gitea-svc/platform/gitea/secrets/정상 예:
-rw------- 1 gitea-svc gitea-svc ... gitea_db_passwordPostgreSQL 쪽에 secret file을 따로 만들지 않았다면 같은 password 값을 직접 새 파일에 기록할 수도 있습니다.
gitea-svc shell에서:
cd /var/lib/gitea-svc/platform/gitea
umask 077
printf '%s' '여기에_PostgreSQL_gitea_role_password' > secrets/gitea_db_password권한을 확인합니다.
ls -l secrets/gitea_db_password
Gitea가 사용하는 DB password와 PostgreSQL의
gitearole password는 반드시 같아야 합니다..env에 password를 직접 기록하지 않고 별도의 secret file로 관리하면 일반 환경 변수에 평문 password를 두지 않을 수 있습니다.
.env 작성
gitea-svc shell에서 작업 디렉터리로 이동합니다.
cd /var/lib/gitea-svc/platform/gitea.env 파일을 새로 생성합니다.
umask 077
cat > .env <<'EOF'
GITEA_DOMAIN=gitea.company.local
GITEA_ROOT_URL=http://gitea.company.local:3000/
GITEA_HTTP_BIND=0.0.0.0
GITEA_HTTP_PORT=3000
GITEA_SSH_BIND=0.0.0.0
GITEA_SSH_PORT=2222
POSTGRES_HOST=192.168.10.50
POSTGRES_PORT=5432
EOF권한을 확인합니다.
ls -l .env환경에 맞게 수정하려면 다음처럼 편집합니다.
editor .envPOSTGRES_HOST에는 PostgreSQL Compose의 POSTGRES_BIND_IP와 동일한 host private/internal IP를 넣습니다.
예를 들어 PostgreSQL이 다음 주소로 publish되어 있다면:
192.168.10.50:5432Gitea도 다음 값을 사용합니다.
POSTGRES_HOST=192.168.10.50
POSTGRES_PORT=5432다음 값은 사용하지 않습니다.
127.0.0.1
platform-postgres127.0.0.1은 Gitea container 자신을 의미합니다.
platform-postgres와 같은 Docker container 이름은 서로 다른 Docker daemon 사이에서 Docker DNS로 해석되지 않습니다.
GITEA_HTTP_BIND, GITEA_SSH_BIND 값은 Docker가 host에 port를 publish하는 IP이므로 POSTGRES_HOST값을 사용합니다.
Docker가 host에 port를 publish 한다는 의미는 "Docker 컨테이너 내부에서 실행 중인 서비스(포트)를 실제 호스트(서버) 컴퓨터의 포트와 연결하여, 외부에서 접근할 수 있도록 공개(노출)한다"는 뜻입니다.
compose.yaml 작성
같은 디렉터리에서 compose.yaml을 직접 만듭니다.
cat > compose.yaml <<'EOF'
services:
gitea:
image: docker.gitea.com/gitea:1.27.3-rootless
container_name: gitea
restart: unless-stopped
environment:
GITEA__server__DOMAIN: ${GITEA_DOMAIN}
GITEA__server__ROOT_URL: ${GITEA_ROOT_URL}
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: ${POSTGRES_HOST}:${POSTGRES_PORT}
GITEA__database__NAME: gitea
GITEA__database__USER: gitea
GITEA__database__PASSWD__FILE: /run/secrets/gitea_db_password
GITEA__database__SSL_MODE: disable
ports:
- "${GITEA_HTTP_BIND}:${GITEA_HTTP_PORT}:3000"
- "${GITEA_SSH_BIND}:${GITEA_SSH_PORT}:2222"
volumes:
- gitea-data:/var/lib/gitea
- gitea-config:/etc/gitea
secrets:
- gitea_db_password
secrets:
gitea_db_password:
file: ./secrets/gitea_db_password
volumes:
gitea-data:
gitea-config:
EOF작성된 파일을 확인합니다.
cat compose.yaml현재 구성처럼 같은 Ubuntu host의 private IP를 통해 PostgreSQL에 접속하고 DB traffic을 별도로 암호화하지 않는다면 다음 값을 사용할 수 있습니다.
GITEA__database__SSL_MODE: disablePostgreSQL을 다른 host나 VLAN으로 이동하거나 조직 정책상 DB traffic 암호화가 필요하다면 PostgreSQL TLS를 구성하고 Gitea의 SSL_MODE도 해당 정책에 맞게 변경해야 합니다.
Gitea 공식 Rootless Docker image는 rootful image와 volume layout 및 SSH 구성이 다르므로 이 문서에서는 다음 image를 사용합니다.
docker.gitea.com/gitea:1.27.3-rootless작성된 파일 확인
현재까지 새로 만든 파일을 확인합니다.
cd /var/lib/gitea-svc/platform/gitea
find . -maxdepth 2 -type f -printf '%M %u:%g %p\n'예상 결과는 다음과 같습니다.
-rw------- gitea-svc:gitea-svc ./.env
-rw-r--r-- gitea-svc:gitea-svc ./compose.yaml
-rw------- gitea-svc:gitea-svc ./secrets/gitea_db_passwordPostgreSQL 연결과 Compose 검증
Gitea daemon에서 PostgreSQL TCP 연결 확인
Gitea container를 시작하기 전에 Gitea 전용 Rootless Docker daemon에서 생성한 임시 container로 PostgreSQL 접속을 확인합니다.
이 테스트는 다음 경로 전체를 검증합니다.
Gitea Rootless Docker network
↓
RootlessKit networking
↓
Ubuntu host private IP
↓
POSTGRES_BIND_IP:5432
↓
PostgreSQL Rootless Docker daemon
↓
PostgreSQL containergitea-svc shell에서 실행합니다.
cd /var/lib/gitea-svc/platform/gitea
set -a
. ./.env
set +a
위 명령어는 특정 디렉토리로 이동한 뒤, 그곳에 있는 환경 변수 파일(.env)을 읽어서 현재 Shell 세션의 환경 변수로 등록하는 과정을 의미합니다.
- set -a : Shell의 옵션을 설정하는 명령입니다.
- a (allexport) 옵션을 켜면, 이 시점 이후에 정의되거나 수정되는 모든 변수가 자동으로 export(환경 변수화) 설정됩니다.
- . ./.env : 앞의 . (dot)은 source 명령어와 동일하게 현재 Shell 실행 환경에서 해당 스크립트를 불러와 실행하라는 의미입니다.
- ./.env는 현재 디렉토리에 위치한 .env 파일을 가리킵니다. 즉, .env 파일 안에 들어있는 KEY=VALUE 형식의 설정값들을 읽어와 변수로 스크립트를 실행합니다.
- set +a : set -a로 켰던 자동 export 기능을 다시 끕니다(+는 옵션 해제).
- 이후에 선언되는 일반 변수들이 자동으로 환경 변수로 전환되지 않도록 원상복구하는 작업입니다.
password를 현재 shell 변수에 읽습니다.
GITEA_DB_PASSWORD="$(cat secrets/gitea_db_password)"PostgreSQL 18 client가 포함된 임시 container로 접속합니다.
docker run --rm \
-e PGPASSWORD="$GITEA_DB_PASSWORD" \
docker.io/library/postgres:18 \
psql \
-h "$POSTGRES_HOST" \
-p "$POSTGRES_PORT" \
-U gitea \
-d gitea \
-c 'select current_user, current_database();'사용이 끝난 shell 변수는 제거합니다.
unset GITEA_DB_PASSWORD정상 예:
current_user | current_database
--------------+------------------
gitea | gitea연결에 실패한다면 Gitea를 먼저 시작하기보다 아래 항목을 순서대로 확인하는 것이 좋습니다.
- PostgreSQL container health
- PostgreSQL Compose의 port publish 설정
POSTGRES_BIND_IP:5432listen 상태- Gitea container에서
POSTGRES_BIND_IP까지 route 가능한지 - host firewall 또는 network ACL
- PostgreSQL
pg_hba.conf또는 접근 제어 설정 gitea_db_password값 일치 여부
host에서 listen 상태를 확인할 때는 다음 명령을 사용할 수 있습니다.
ss -lnt | grep ':5432'Gitea Compose 설정 확인
현재 shell이 반드시 gitea-svc의 Rootless Docker daemon을 바라보도록 지정합니다.
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock프로젝트 디렉터리로 이동합니다.
cd /var/lib/gitea-svc/platform/giteaCompose 최종 설정을 검증합니다.
docker compose configdocker compose config 결과에서 다음 값들이 의도대로 확장되었는지 확인합니다.
image: docker.gitea.com/gitea:1.27.3-rootless
GITEA__database__DB_TYPE=postgres
GITEA__database__HOST=<host-private-ip>:5432
GITEA__database__NAME=gitea
GITEA__database__USER=gitea
GITEA__database__PASSWD__FILE=/run/secrets/gitea_db_password또한 3000, 2222 port publish와 gitea-data, gitea-config volume이 포함되어 있는지 확인합니다.
Gitea 공식 Rootless Docker image는 rootful image와 volume layout 및 SSH 구성이 다릅니다.
이 문서에서는 다음 image를 고정합니다.
docker.gitea.com/gitea:1.27.3-rootlessGitea 공식 문서는 rootful image와 rootless image가 서로 호환되지 않으며, 한 방식을 선택한 뒤 단순히 Compose의 image tag만 바꾸는 방법으로 전환하지 말라고 안내합니다.
Gitea 시작과 운영 확인
Gitea 시작
먼저 image를 가져옵니다.
docker compose pullGitea를 시작합니다.
docker compose up -d상태를 확인합니다.
docker compose ps로그를 확인합니다.
docker compose logs -f gitea데이터 영속화
Gitea 공식 Rootless Docker image에서 주요 영속화 경로는 다음과 같습니다.
/var/lib/gitea
/etc/gitea이 문서의 Compose에서는 named volume을 사용합니다.
gitea-data -> /var/lib/gitea
gitea-config -> /etc/giteaGitea 공식 문서는 Rootless Docker에서 named volume 사용을 지원하며, named volume을 사용하면 Docker가 volume ownership 처리를 담당합니다.
현재 volume을 확인합니다.
docker volume lsCompose 프로젝트에 연결된 volume을 확인합니다.
docker compose config --volumesHTTP와 SSH 접속 확인
host에서 포트를 확인합니다.
ss -lnt | grep -E ':3000|:2222'웹 접속 예:
http://gitea.company.local:3000/SSH port는 예제에서 2222를 사용합니다.
gitea.company.local:2222Rootless Docker에서는 일반 사용자 프로세스가 host의 privileged port인 1024 미만 포트를 직접 bind하는 데 제약이 있습니다.
현재 예제의 3000과 2222는 모두 1024 이상이므로 별도의 privileged-port 설정 없이 사용할 수 있습니다.
WSL 환경에서는 gitea.company.local 이름을 WSL의 IP로 해석하지 못할 가능성이 존재합니다. Windows의 C:\Windows\System32\drivers\etc\hosts 파일에 192.168.10.50 gitea.company.local 추가합니다.
Gitea 관리자와 Agent Bot 분리
초기 관리자 계정과 자동화 계정은 분리하는 것이 좋습니다.
예:
관리자:
gitea-admin
Agent Bot:
codex-agent-botcodex-agent-bot에는 필요한 repository, issue, pull request 범위만 부여합니다.
main과 dev에는 protected branch 정책을 적용하여 자동화 계정이 직접 push하지 못하도록 구성하고, 필요한 경우 Pull Request와 사람 review를 거쳐 merge되도록 설계합니다.
Webhook과 Agent Server 연동
Agent Server가 같은 Ubuntu host에서 native systemd service로 실행되더라도 Gitea는 container 안에서 동작합니다.
따라서 webhook target에 다음 주소를 사용하면 안 됩니다.
http://127.0.0.1:8080/webhooks/giteaGitea container에서 127.0.0.1은 Ubuntu host가 아니라 Gitea container 자신을 의미합니다.
Agent Server가 Ubuntu host의 private IP에서 8080 port를 listen한다면 다음과 같이 설정합니다.
http://192.168.10.50:8080/webhooks/gitea예를 들어 Agent Server가 다음과 같이 listen한다고 가정합니다.
LISTEN_ADDR=:8080이 경우 0.0.0.0:8080으로 노출될 수 있으므로 host firewall에서 필요한 source 범위만 허용하거나 reverse proxy와 TLS를 적용하는 것이 좋습니다.
Webhook secret 검증
Gitea Webhook의 secret과 Agent Server의 다음 설정은 동일한 값을 사용합니다.
GITEA_WEBHOOK_SECRETGitea는 webhook secret이 설정되어 있으면 raw request body를 기준으로 HMAC-SHA256 digest를 계산하여 X-Gitea-Signature header로 전달합니다.
Agent Server에서는 request body를 변경하기 전에 해당 signature를 검증해야 합니다.
Pull Request review 이벤트
Issue 또는 label trigger만 구독하는 것으로는 Agent가 생성한 Pull Request의 승인 흐름을 자동화할 수 없습니다.
필요한 workflow라면 다음 이벤트를 함께 구독합니다.
Issues
Pull Request
Pull Request ReviewGitea의 pull_request_review는 webhook 설정 화면의 umbrella subscription이며 실제 delivery에서는 더 구체적인 event type이 전달됩니다.
사람이 Pull Request를 승인하면 다음 event type을 받을 수 있습니다.
pull_request_review_approved예를 들어 workflow가 다음과 같다면:
Issue에 codex-ready label
↓
Agent Server
↓
agent/issue-N branch 생성
↓
Codex 작업
↓
Pull Request: agent/issue-N -> dev
↓
사람 review / approve
↓
pull_request_review_approved webhook
↓
Agent Server가 merge API 호출dev protected branch에서는 직접 push 권한과 PR merge 권한을 분리합니다.
Agent bot은 dev에 직접 push하지 않고, required_approvals 정책을 유지한 상태에서 필요한 merge 권한만 갖도록 구성합니다.
Agent Server가 Gitea merge API를 사용할 때 보호 규칙을 우회하는 옵션을 사용하지 않는다면 사람 승인과 branch protection을 그대로 적용할 수 있습니다.
Rootless Docker와 Gitea lifecycle
Gitea 전용 Docker daemon
gitea-svc session에서 daemon 상태를 확인합니다.
systemctl --user status docker재시작:
systemctl --user restart docker로그 확인:
journalctl --user -u docker -f현재 daemon socket을 다시 확인하려면 다음 명령을 사용합니다.
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
docker infoGitea Compose workload
프로젝트 디렉터리로 이동합니다.
cd /var/lib/gitea-svc/platform/gitea상태 확인:
docker compose psGitea만 재시작:
docker compose restart gitea로그 확인:
docker compose logs -f gitea전체 Compose 종료:
docker compose down다시 시작:
docker compose up -ddocker compose down은 기본적으로 named volume을 삭제하지 않습니다.
데이터를 유지해야 하는 운영 환경에서는 다음 명령을 함부로 사용하지 않습니다.
docker compose down -v-v를 사용하면 Compose가 관리하는 named volume까지 삭제될 수 있으므로 Gitea data와 config를 잃을 수 있습니다.
설치 후 확인
전체 구성을 확인할 때는 다음 순서로 점검하면 됩니다.
# gitea-svc 사용자
id gitea-svc
grep '^gitea-svc:' /etc/subuid
grep '^gitea-svc:' /etc/subgidgitea-svc shell:
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
systemctl --user status docker
docker infoCompose:
cd /var/lib/gitea-svc/platform/gitea
docker compose config
docker compose ps
docker compose logs --tail=100 giteahost port:
ss -lnt | grep -E ':3000|:2222|:5432'Gitea daemon에서 PostgreSQL 연결:
set -a
. ./.env
set +a
GITEA_DB_PASSWORD="$(cat secrets/gitea_db_password)"
docker run --rm \
-e PGPASSWORD="$GITEA_DB_PASSWORD" \
docker.io/library/postgres:18 \
psql \
-h "$POSTGRES_HOST" \
-p "$POSTGRES_PORT" \
-U gitea \
-d gitea \
-c 'select current_user, current_database();'
unset GITEA_DB_PASSWORD최종적으로 다음 상태이면 기본 구성이 완료된 것입니다.
gitea-svc 전용 Rootless Docker daemon 실행
Gitea 1.27.3-rootless container 정상 실행
Gitea HTTP 3000 접근 가능
Gitea SSH 2222 접근 가능
Gitea container -> HOST_PRIVATE_IP:5432 PostgreSQL 연결 가능
Gitea data/config named volume 존재
Webhook -> Agent Server host endpoint 접근 가능정리
이 구조의 핵심은 Gitea와 PostgreSQL을 서로 다른 Rootless Docker daemon으로 격리하면서도 필요한 통신 경로만 host TCP endpoint를 통해 연결하는 것입니다.
postgres-svc
└─ Docker daemon A
└─ PostgreSQL
└─ HOST_PRIVATE_IP:5432
gitea-svc
└─ Docker daemon B
└─ Gitea
└─ HOST_PRIVATE_IP:5432로 접속각 서비스 사용자는 자신의 Docker socket을 사용합니다.
/run/user/<postgres-svc UID>/docker.sock
/run/user/<gitea-svc UID>/docker.sock따라서 관리 shell에서도 XDG_RUNTIME_DIR과 DOCKER_HOST를 명확히 설정하는 것이 중요합니다.
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock또한 rootful Docker daemon 비활성화는 필수 단계가 아닙니다. 기존 rootful workload가 있다면 그대로 유지할 수 있고, Rootless Docker 설치 도구가 system-wide daemon을 감지해 설치를 막는 경우 --force를 사용할 수 있습니다.
Gitea는 공식 1.27.3-rootless image를 사용하며, rootful/rootless image는 volume layout과 SSH 방식이 다르므로 단순한 image tag 변경 방식으로 서로 전환하지 않습니다.
이 구성을 사용하면 PostgreSQL, Gitea, 이후 Agent Server용 Docker workload를 서비스 사용자와 daemon 단위로 분리하여 운영할 수 있습니다.
참고 자료
- Gitea Rootless Docker 설치: https://docs.gitea.com/installation/install-with-docker-rootless/
- Gitea 1.27.3 릴리스: https://blog.gitea.com/release-of-1.27.3/
- Gitea Webhooks: https://docs.gitea.com/usage/repository/webhooks/
- Docker Rootless mode: https://docs.docker.com/engine/security/rootless/
- Docker Rootless UID/GID mapping: https://docs.docker.com/engine/security/rootless/uid-gid-mapping/
- Docker bridge networking: https://docs.docker.com/engine/network/drivers/bridge/
- Docker port publishing: https://docs.docker.com/engine/network/port-publishing/
'AI Development > Workflow' 카테고리의 다른 글
| Rootless Docker daemon에 PostgreSQL 18 설치하기 (0) | 2026.09.02 |
|---|---|
| Codex Agent Server 인프라 설계 — PostgreSQL / Gitea / Coding Agent의 3-daemon 분리 (0) | 2026.09.02 |
| Codex Agent Server 구축하기 (0) | 2026.09.02 |
댓글