uv frozen-lockfile: CI·Docker에서 uv.lock만으로 설치하기

워크스페이스 그룹 정보가 기록된 uv.lock을 CI·Docker 빌드에 활용하고, manifest 없이 frozen 명령을 실행할 때의 조건과 한계를 점검한다.

2026-10-15

uv.lock만 복사하는 빌드에도 설치 조건은 남는다

uv sync --frozen이 workspace manifest 없이 uv.lock을 발견해 설치를 진행할 수 있게 됐다. uv 0.12.23 릴리스 노트에 추가된 frozen-lockfile preview 기능이다. CI 작업 디렉터리나 Docker 빌드 구간에 manifest를 가져오지 않는 구성을 검토할 때 직접 관련되는 변화다.

이 글의 기준은 uv 0.12.22 (2026-10-01), 0.12.23 (2026-10-03), lockfile revision >= 5다. 여기서 revision 조건은 PR #22018이 설명하는 lockfile-only sync의 요구사항이다. 실행할 uv의 버전과 설치에 사용할 lockfile의 revision을 따로 확인해야 한다.

적용 여부를 판단할 때는 “manifest를 빼도 되는가”에서 멈추지 말고, 설치 대상에 로컬 패키지가 포함되는지까지 봐야 한다. 로컬 패키지는 소스 트리와 빌드 메타데이터가 있어야 설치할 수 있다. uv.lock만으로 시작할 수 있다는 말이 모든 설치 입력을 lockfile 하나로 대체한다는 뜻은 아니다.

다음 그림은 sync가 읽을 설정과, manifest가 없을 때 확인해야 할 설치 조건을 나눠 보여 준다.

⤢✕있음없음아니오예예아니오uv sync --frozenworkspace manifest가있는가manifest의 설정 사용lockfile revision 5 이상인가lockfile-only sync 조건미충족uv.lock에서 설치 정보 읽기로컬 패키지를 설치하는가소스 트리와 빌드 메타데이터필요로컬 패키지 소스 조건은 해당없음

워크스페이스 멤버의 그룹 선택을 lockfile에서 읽는다

uv 0.12.22 릴리스 노트는 워크스페이스 멤버의 기본 그룹과 dependency group별 Python 요구사항을 lockfile에 기록한다고 명시한다. 같은 릴리스에는 frozen sync에서 선택한 워크스페이스 멤버마다 기록된 기본 그룹을 사용하도록 고친 내용도 들어 있다.

실무에서 확인할 대상은 패키지 버전 목록만이 아니다. 워크스페이스 멤버마다 기본 그룹이나 그룹별 Python 요구사항이 다르다면, CI가 선택하는 멤버에 맞는 정보가 lockfile에 기록돼 있는지도 봐야 한다. “워크스페이스의 기본값” 하나로 묶어서 생각하면 멤버별 설치 조건을 놓치기 쉽다.

PR #22018은 manifest가 없는 sync에서도 선택한 멤버의 기록된 기본 그룹과 그룹별 Python 요구사항을 사용한다고 설명한다. 이 부분은 manifest가 있는 frozen sync와 동작을 맞추는 지점이다. 따라서 manifest를 제외하는 빌드 구간을 만들기 전에, 해당 멤버의 그룹 선택과 Python 요구사항이 설치 입력으로 남아 있는지 점검하는 것이 순서다.

단, 이 설명을 비프로젝트 워크스페이스 루트에 그대로 적용하면 안 된다. 그 경우에는 자료 사이에 차이가 있다.

비프로젝트 루트는 기본 그룹을 가정하지 않는다

0.12.22 릴리스 노트에는 비프로젝트 워크스페이스 루트의 기본 그룹과 그룹별 Python 요구사항도 lockfile에 기록한다고 적혀 있다. 반면 PR #22018 본문은 비프로젝트 루트의 기본 그룹이 기록되지 않으므로 그룹을 명시적으로 선택해야 하며, 그룹별 Python 요구사항도 사용할 수 없다고 설명한다.

제공된 자료만으로는 이 차이가 어느 구현 시점이나 조건에서 해소됐는지 판단할 수 없다. 비프로젝트 루트에 대해서는 “manifest를 빼도 기본 그룹과 Python 요구사항이 그대로 적용된다”고 단정할 근거가 부족하다.

점검 대상 0.12.22 릴리스 노트 PR #22018의 lockfile-only sync 설명 CI·Docker에서의 판단
워크스페이스 멤버의 기본 그룹 lockfile에 기록 선택한 멤버의 기록된 기본 그룹 사용 실제로 선택하는 멤버를 기준으로 점검
워크스페이스 멤버의 그룹별 Python 요구사항 lockfile에 기록 기록된 요구사항 사용 그룹 선택과 Python 요구사항을 함께 점검
비프로젝트 루트의 기본 그룹 lockfile에 기록 기록되지 않아 명시적 그룹 선택 필요 기본 그룹의 자동 적용을 전제로 구성하지 않기
비프로젝트 루트의 그룹별 Python 요구사항 lockfile에 기록 사용할 수 없음 manifest 없는 실행에서 적용된다고 단정하지 않기

비프로젝트 루트에서 manifest를 제외하려면 PR이 요구하는 명시적인 그룹 선택을 적용 기준으로 삼는 편이 보수적이다. 그룹별 Python 요구사항에 의존하는 구성이라면, 이 자료만으로 manifest 없는 경로가 같은 조건을 보존한다고 판단하지 말고 manifest를 유지하는 구성을 우선 검토해야 한다.

CI 작업의 목적에 맞춰 frozen 명령을 고른다

0.12.23에서 manifest 없는 실행이 추가된 대상은 설치뿐이 아니다. 의존성 내보내기, 트리 조회, 워크스페이스 메타데이터 조회에도 frozen-lockfile이 제공된다.

CI 작업을 설계할 때는 필요한 결과부터 정하면 된다. 환경을 만들려는 작업인지, 의존성 정보를 내보내려는 작업인지, lockfile의 구조를 확인하려는 작업인지에 따라 명령을 선택한다.

작업 목적 자료에 제시된 명령 manifest 없는 실행에서의 용도
환경 동기화 uv sync --frozen uv.lock을 발견해 환경 동기화
의존성 내보내기 uv export --frozen uv.lock에서 의존성 내보내기
의존성 트리 확인 uv tree --frozen uv.lock에서 의존성 트리 조회
워크스페이스 정보 확인 uv workspace metadata --frozen uv.lock에서 워크스페이스 메타데이터 조회
메타데이터 확인과 환경 동기화 uv workspace metadata --frozen --sync manifest 없이 메타데이터 조회와 환경 동기화

sync의 preview 기능을 명시적으로 활성화하려면 PR에 제시된 옵션을 함께 사용한다.

uv sync --frozen --preview-features frozen-lockfile

PR은 --preview-features frozen-lockfile이 활성화되지 않으면 preview 경고를 출력한다고 설명한다. 이 옵션은 lockfile revision 조건을 대신하지 않으며, 로컬 패키지의 소스 요구도 없애지 않는다.

환경을 동기화하지 않고 lockfile을 확인하는 작업에 해당하는 명령은 다음과 같다.

uv export --frozen
uv tree --frozen
uv workspace metadata --frozen

lockfile-only sync의 revision 조건을 다른 명령 전체에 확대해서 적용할 근거는 제공된 자료에 없다. 문서화된 조건과 각 작업의 목적을 구분해 적용해야 한다.

Docker에서는 manifest와 소스를 별개의 입력으로 다룬다

Docker 빌드에서 이번 기능을 활용할 지점은 workspace manifest가 없는 구간이다. 다만 어떤 파일을 생략할 수 있는지는 설치 대상과 설정에 따라 나눠 판단해야 한다.

PR #22018은 workspace manifest가 있으면 그 설정을 사용하며, 여기에는 추가 빌드 의존성의 소스 매핑도 포함된다고 설명한다. 멤버 manifest가 남아 있으면 해당 멤버의 빌드 의존성 소스도 사용한다. 따라서 manifest를 남기는 구성과 제거하는 구성을 같은 설정 입력으로 취급해서는 안 된다.

빌드 입력을 검토할 때는 다음 경계를 확인한다.

  • uv.lock은 manifest 없는 sync에서 선택한 멤버의 기록된 기본 그룹과 그룹별 Python 요구사항을 제공한다.
  • workspace manifest가 있으면 추가 빌드 의존성의 소스 매핑을 포함한 설정을 사용한다.
  • 멤버 manifest가 남아 있으면 그 멤버의 빌드 의존성 소스를 사용한다.
  • 로컬 패키지를 설치하려면 소스 트리와 빌드 메타데이터가 필요하다.

다음 그림은 설치 입력을 줄일 때 서로 대신할 수 있다고 가정하면 안 되는 경계를 보여 준다.

⤢✕ manifest 없는 설치의 입력 경계 uv.lock · manifest · 로컬 패키지 소스 비교 uv.lock uv.lock 선택한 워크스페이스 멤버의 기록된 기본 그룹 그룹별 Python 요구사항 입력 구분 그룹 선택과 Python 요구사항의 입력 manifest workspace manifest 존재할 때 사용하는 설정 추가 빌드 의존성의 소스 매핑 멤버 manifest 남아 있을 때 사용하는 해당 멤버의 빌드 의존성 소스 입력 구분 존재 여부에 따라 사용하는 설정 입력 로컬 패키지 소스 로컬 패키지 소스 트리와 빌드 메타데이터 로컬 패키지 설치에 필요 입력 구분 해당 패키지 설치에 필요한 입력 uv frozen-lockfile: CI·Docker에서 uv.lock만으로 설치하기 | delivery-quality

설치하려는 로컬 패키지의 소스를 뒤쪽 빌드 구간에만 배치했다면, 그보다 앞에서 해당 패키지를 설치하는 sync는 필요한 입력을 갖추지 못한다. 반대로 추가 빌드 의존성의 소스 매핑을 manifest에 두고 사용하는 구성이라면, manifest를 제거하기 전에 그 설정이 필요한 설치인지부터 확인해야 한다.

이번 자료에는 Dockerfile 예제나 캐시 성능 측정값이 없다. 따라서 manifest를 덜 복사할 수 있다는 변화만으로 빌드 시간이나 캐시 효율이 개선된다고 판단할 수는 없다. 활용의 출발점은 성능 수치가 아니라 각 빌드 구간에 필요한 설치 입력을 식별하는 것이다.

전환 기준은 파일 개수보다 설치 조건이다

기존 CI·Docker 구성을 바꿀 때는 먼저 어떤 워크스페이스 멤버를 선택하고, 어떤 그룹을 설치하며, 로컬 패키지까지 설치하는지 정리한다. 그다음 manifest 없는 sync에 사용할 lockfile이 revision 5 이상인지 확인한다.

워크스페이스 멤버의 기본 그룹과 그룹별 Python 요구사항은 lockfile에서 읽을 수 있는 정보다. 로컬 패키지의 소스 트리와 빌드 메타데이터는 별도로 필요한 입력이다. 남아 있는 manifest의 설정도 실제 실행에 사용되므로, 파일을 제거할 때는 빌드 의존성 소스 설정까지 함께 검토해야 한다.

비프로젝트 루트는 제공된 자료의 설명이 일치하지 않는 부분이다. 기본 그룹의 자동 적용을 전제로 전환하지 말고 명시적인 그룹 선택을 검토한다. 그룹별 Python 요구사항의 보존 여부를 이 자료로 확정할 수 없다면 manifest를 유지한다.

frozen-lockfile을 적용할 수 있는 구간은 이렇게 좁혀진다. manifest가 없어도 필요한 그룹 정보와 설치 소스를 갖춘 구간에는 lockfile 기반 실행을 배치하고, manifest의 설정이나 로컬 소스가 필요한 구간에는 그 입력을 함께 제공한다. 이 경계를 지키면 uv.lock만으로 시작할 수 있는 작업과 추가 파일이 필요한 설치를 구분해 빌드를 구성할 수 있다.

uvPythonlockfileCIDocker