2 điểm bởi GN⁺ 2025-03-25 | 1 bình luận | Chia sẻ qua WhatsApp
  • Vì Podman không có daemon, cần có một chủ thể thực thi để khởi chạy container ổn định sau khi máy chủ boot; Quadlet giải quyết việc này theo kiểu file service của systemd
  • Cách podman generate systemd trước đây phải xử lý riêng việc tạo container và tạo file service, và mỗi khi thay đổi file sinh ra lại phát sinh gánh nặng chỉnh sửa thủ công
  • Quadlet đặt chung cấu hình [Container], [Service], [Install] trong file .container~/.config/containers/systemd, giúp quản lý tùy chọn Podman và hành vi systemd tại một nơi
  • Với rootless Podman, để tự động khởi động khi boot cần WantedBy=default.targetbật linger; multi-user.target không được định nghĩa trong chế độ user
  • Khi tận dụng AutoUpdate=registry, dependency của systemd, gom nhóm file theo thư mục, và cả công cụ chuyển đổi podlet, Quadlet rất phù hợp để vận hành Podman rootless·daemonless

Vì sao cần Quadlet

  • Quadlet là cách cho phép chạy container Podman như một service systemd
    • Có thể chạy container ở nền
    • Có thể tự động khởi động container sau khi máy chủ reboot
  • Bản thân cách chạy container Podman dưới systemd không phải là mới
    • Trước đây dùng lệnh podman generate systemd
    • Lệnh này hiện hiển thị cảnh báo ngừng dùng khuyến nghị migrate sang Quadlet
  • Vì Podman có kiến trúc daemonless, cần một chủ thể thực thi để khởi động container mà không cần daemon

Bất tiện của cách podman generate systemd

  • Cách cũ trước tiên phải tạo container bằng podman create
    • Container ví dụ dùng image docker.io/library/postgres:16
    • Bao gồm -p 5432:5432, mount volume, biến môi trường POSTGRES_PASSWORD, và label io.containers.autoupdate=registry
  • Sau đó chạy podman generate systemd test-db -fn --new để tạo file container-test-db.service
  • File service được tạo sẽ đặt trong ~/.config/systemd/user, rồi kích hoạt và khởi động bằng lệnh sau
    • systemctl --user enable --now container-test-db
  • Luồng này khiến bạn phải lặp lại việc tạo container, tạo file service, di chuyển file khi cần, và kích hoạt service
  • Khi lệnh tạo container trở nên dài, về sau để chạy lại cần quản lý riêng một shell script
  • Nếu muốn tùy biến file service systemd đã sinh ra, mỗi lần đều phải sửa thủ công lại

Cấu trúc file Quadlet

  • Với cách Quadlet, tạo thư mục ~/.config/containers/systemd và đặt file .container bên trong
  • File test-db.container ví dụ bao gồm các thành phần sau
    • [Container]
      • Image=docker.io/library/postgres:16
      • AutoUpdate=registry
      • PublishPort=5432:5432
      • Volume=%h/volumes/test-db:/var/lib/postgresql/data:Z
      • Environment=POSTGRES_PASSWORD=CHANGE_ME
    • [Service]
      • Restart=always
    • [Install]
      • WantedBy=default.target
  • File .container là file service systemd thông thường, nhưng có thêm section đặc biệt [Container]
  • Nhiều tùy chọn trong [Container] tương ứng với các tùy chọn dòng lệnh dùng trong podman create
    • Image: image và tag sẽ dùng
    • AutoUpdate=registry: tương ứng với --label "io.containers.autoupdate=registry"
    • PublishPort: tương ứng với -p
    • Volume: tương ứng với -v
    • Environment: tương ứng với -e
  • Với thư mục home của user, cần dùng định danh systemd %h thay vì ~
  • Restart=always trong [Service] khiến container luôn được khởi động lại trừ khi bị dừng thủ công
  • WantedBy=default.target trong [Install] khiến container tự động khởi động khi boot

Cấu hình systemd cần thiết với rootless Podman

  • Với container rootless, cần dùng default.target thay vì multi-user.target
    • multi-user.target không được định nghĩa trong chế độ user của systemd
    • Có thể kiểm tra bằng systemctl --user status multi-user.target
    • Ở chế độ system, có thể kiểm tra bằng systemctl status multi-user.target
  • Vì container chạy dưới dạng systemd user service, để container khởi động ngay cả khi user chưa đăng nhập, cần bật linger
    • loginctl enable-linger
  • Nếu cần tự động khởi động sau khi máy chủ reboot, bắt buộc phải bật linger
  • Để systemd phát hiện file service mới, chạy lệnh sau
    • systemctl --user daemon-reload
  • Có thể khởi động container và kiểm tra trạng thái từ cả systemd lẫn Podman
    • systemctl --user start test-db
    • systemctl --user status test-db
    • podman ps
  • Tên container mặc định có dạng thêm systemd- vào trước tên file service
    • Tên container mặc định của test-db.containersystemd-test-db
    • Đây là quy tắc đặt tên để tránh xung đột
    • Có thể chỉ định trực tiếp bằng tùy chọn ContainerName trong section [Container]

Lợi ích vận hành của Quadlet

  • Quadlet cho phép quản lý cấu hình service container bằng một file duy nhất
    • Không cần quản lý riêng script tạo file service và file service đã sinh ra như cách cũ
  • Có thể dùng các tùy chọn có trong section [Unit][Service] của systemd
    • Ví dụ có thể chỉ định lệnh chạy trước khi khởi động container bằng StartExecPre
    • Giảm bớt quy trình sửa thủ công file đã sinh ra về sau
  • Thay vì viết và debug shell script, có thể vận hành container xoay quanh file cấu hình
  • Dễ biểu diễn dependency giữa các container theo kiểu systemd

Biểu diễn dependency giữa các container

  • Nếu container ứng dụng phụ thuộc vào container cơ sở dữ liệu, có thể chỉ định quan hệ bằng section [Unit] của systemd
  • Container OxiTraffic là ví dụ phụ thuộc vào container test-db
    • Đặt Requires=test-db.service trong [Unit] để ứng dụng chỉ khởi động khi cơ sở dữ liệu được khởi động
    • Đặt After=test-db.service để hai container không khởi động song song
  • Khi tham chiếu, dùng tên service chứ không phải tên file .container
    • test-db.container: tên file
    • test-db.service: tên service
    • systemd-test-db: tên container mặc định
  • Để ứng dụng giao tiếp với cơ sở dữ liệu, cả hai container đều cần thêm tùy chọn Network trong section [Container], nhưng bài này không bàn về networking

Nhiều file và gom nhóm

  • Quadlet có thể được cấu hình theo cách mỗi container có một file .container riêng
  • So với cách đưa toàn bộ ứng dụng multi-container vào một file Docker Compose, các file riêng theo từng container có thể giảm gánh nặng nhận thức
  • Nếu file Docker Compose dài hàng trăm dòng và chứa hàng chục container, việc bảo trì có thể trở nên khó khăn
  • Docker Compose cũng hỗ trợ chức năng chia thành nhiều file
  • Quadlet có thể đặt unit file trong các thư mục bên trong ~/.config/containers/systemd
    • Trong ví dụ có thể tạo thư mục oxitraffic rồi đặt chung file của ứng dụng và cơ sở dữ liệu

Cập nhật image

  • Khi đặt AutoUpdate=registry, có thể kiểm tra cập nhật image bằng podman auto-update
  • Nếu registry có image mới tương thích với tag đang dùng, Podman sẽ pull image và khởi động lại container
  • Trong Docker, có thể cần công cụ như Watchtower cho mục đích này, nhưng Podman cung cấp sẵn chức năng đó
  • Các tag như latest có thể nguy hiểm
    • Trong OxiTraffic, latest có thể bao gồm breaking change của phiên bản tiếp theo
    • Nếu dùng latest với PostgreSQL, có thể bị nâng lên major version mới, mà major upgrade của PostgreSQL luôn cần migration thủ công
  • Trong môi trường vận hành, cần dùng tag không dẫn đến breaking change
  • Cũng có thể chạy thủ công podman auto-update vài ngày một lần để xem những gì đã được cập nhật, sau đó kiểm tra container có hoạt động bình thường hay không

podman-compose và công cụ migration

  • podman-compose là một Python script chạy file Compose bằng Podman
  • Các lý do khiến nó khó được xem là giải pháp thay thế Docker Compose dài hạn như sau
    • Đây là tầng dịch giữa đặc tả Compose với Podman·systemd, và không cho phép dùng toàn bộ chức năng của systemd
    • Dự án Podman chính thức được viết bằng ngôn ngữ biên dịch như Rust hoặc Go
    • Commit cuối cùng cách đây 5 tháng, nên không được bảo trì tích cực
  • Quadlet phù hợp hơn với thiết kế rootless·daemonless của Podman
  • Nếu muốn thử Quadlet từ file Compose, có thể dùng podlet
    • Đây là công cụ Rust có thể tạo file Quadlet từ lệnh Podman hoặc file Docker Compose

Tài liệu đọc thêm

1 bình luận

 
GN⁺ 2025-03-25
Các ý kiến trên Hacker News
  • Quadlet gần như là thứ tốt nhất xuất phát từ Podman, và tôi rất khuyến nghị cho những ai tò mò về Podman hoặc muốn chuyển sang workload dựa trên container
    Bạn có thể gắn container vào và quản lý chúng như các dịch vụ hệ thống thông thường, không cần học thêm một lớp orchestration riêng để cho chúng chạy cùng nhau hay phải phụ thuộc vào tài nguyên không phải container
    Chỉ cần viết systemd unit như cách bạn vẫn làm, còn tự động cập nhật hoặc khởi động lại/thông báo khi dịch vụ lỗi là phần cộng thêm
    Cách cố làm điều tương tự với Docker thường là né Docker daemon bằng một lệnh run khổng lồ, rồi hay để lại các dịch vụ và container “ma”; Quadlet thì gọn gàng hơn nhiều, cấu hình cũng nằm cùng các vị trí unit của systemd như /etc/systemd/, .config/systemd, /usr/local/lib/systemd, nên dễ sao lưu
    Tuy nhiên, nó không phải câu trả lời thay cho docker-compose trong phát triển cục bộ, và đội Podman cũng có vẻ không quá quan tâm đến mảng đó
    Container người dùng phù hợp cho hạ tầng kiểm thử cục bộ chạy dài hạn như cơ sở dữ liệu nền, nhưng quá phiền cho vòng lặp thông thường biên dịch → docker compose up → kiểm thử → docker compose down
    Câu trả lời thực tế là .kube Quadlet (Kubernetes play), hoặc dùng docker compose với Podman socket
    Tôi thích đến mức đã dành thời gian rảnh suốt vài tháng để làm một công cụ GitOps quản lý Quadlet, và nó cho cảm giác như cách đúng đắn để quản lý các máy chủ được container hóa
    Điều quan trọng là [0] không phải podman-compose. Bài viết cũng chỉ ra rằng podman-compose không tốt lắm và thiếu phát triển. Podman triển khai phần lớn đặc tả compose, nên trong hầu hết tình huống bạn có thể dùng docker compose
    Có lẽ những người từng thử khi RH mới bắt đầu thúc đẩy Podman đã rời bỏ vì sự lưng chừng của Podman 3 và trải nghiệm tệ với podman-compose

    • Vì muốn đi theo hướng gần với cách của RH, tôi đã thử dùng Quadlet và podlet, nhưng trải nghiệm chuyển từ docker compose trên Podman sang thật kinh khủng
      Sau khi đổi nhiều flag, nâng phiên bản, rồi đi theo luồng mà chính podlet cũng khuyên chuyển sang công cụ khác, mọi thứ thành một cái hang thỏ không hồi kết
      Cuối cùng systemd của tôi chỉ đơn giản chạy podman compose up
      Tôi tò mò lợi ích của Quadlet là gì so với việc cứ để compose tự làm việc của nó
      Nhân tiện, Podman chạy dưới user của tôi và hoàn toàn không có daemon
    • Mẹo cho môi trường phát triển là bật Podman socket, nếu là container rootless thì bật ở cấp người dùng, rồi dùng docker-compose độc lập
    • Trên Windows hoặc Mac không dùng được systemd, nên rốt cuộc chỉ còn podman compose
  • Gần đây tôi phát hiện Quadlet và đã dựng toàn bộ homelab dựa trên atomic OS cùng Quadlet rootless; rất khuyến nghị
    Cũng có thể dùng kích hoạt socket của systemd, chẳng hạn tạo socket http/https của systemd và tự động kích hoạt Traefik giống như ssh.socketpodman.socket lần lượt kích hoạt sshd.service, podman.service
    Trong cấu hình rootless, đây gần như là cách duy nhất để giữ nguyên IP gốc, nên gần như là cứu tinh. Podman/Docker rootless thường không dễ cho phép giữ IP gốc mà không có nhược điểm lớn
    https://github.com/savely-krasovsky/homelab

    • Tôi tò mò mất bao lâu để Traefik được kích hoạt
  • Quadlet là một lựa chọn thay thế tốt cho mục đích docker compose kiểu “chạy tất cả các container phụ thuộc lẫn nhau trong một môi trường gần giống production”
    Nhưng tôi mong phần phát triển — một mục đích khác của docker compose — sẽ tốt hơn
    docker compose có thể khởi chạy cùng lúc cơ sở dữ liệu, Redis, OpenSearch, các dependency khác, proxy nginx, container phát triển mount .:/app làm volume; rồi cũng có thể dọn chúng cùng nhau và đưa docker-compose.yml vào repository
    Quadlet lại bảo đặt tất cả file trong ~/.config/containers/systemd, nên nó không còn bị cô lập theo dự án nữa, cũng không tiện để check-in và chia sẻ với các developer khác. Hơn nữa, họ cũng phải dùng Podman
    Phần lớn mọi người vẫn dùng Docker, và các môi trường phát triển được host như Codespaces cũng cung cấp Docker
    Vì vậy tôi dùng docker compose bằng file YAML đã được check-in. Do tôi dùng Podman nên phải tự thêm :Z vào mọi volume, nhưng Docker thông thường lại không xử lý được phần đó
    Sẽ tốt nếu có một lựa chọn thay thế docker compose cho phát triển, nhưng Quadlet có vẻ không phù hợp lắm

    • Nếu dùng podman-compose --in-pod=1 systemd -a create-unit, bạn có thể tạo service podman-compose@, rồi sau đó đăng ký file compose.yml cùng với $name bằng podman-compose systemd -a register
      Khi đó bạn có thể quản lý pod dựa trên file compose qua podman-compose@$name.service, và nó cũng hoạt động hoàn toàn rootless
    • Trong trường hợp này, thứ bạn đang tìm là Podman pod, và tôi sẽ orchestration toàn bộ bằng Ansible rồi phân phối playbook Ansible cho mọi người để chạy cục bộ
    • systemd-run cho phép chạy service systemd tạm thời dựa trên một lệnh bất kỳ
      Nếu cần, nó còn tạo ra một unit tạm thời gọn gàng, tuân theo các quy tắc phạm vi của systemd, bao gồm cả timer
      [1] https://www.freedesktop.org/software/systemd/man/systemd-run...
    • Tôi thấy Quadlet nhìn chung thiên về triển khai hơn. Nếu đang tìm lựa chọn thay thế compose, nên xem Podman Kube Play[1]
      Nó có định dạng tương tự Kubernetes, kèm thêm một chút tiện ích như build container
      Đặt file kube play ở thư mục gốc dự án rồi chạy podman kube play project.yaml --build thì pod, volume, v.v. sẽ được chạy cùng nhau
      File Quadlet hữu ích ở giai đoạn triển khai. Nếu muốn đưa dự án lên VPS, bạn có thể dùng Kube Quadlet[2] cùng với project.yaml đã có
      [1] https://docs.podman.io/en/latest/markdown/podman-kube-play.1...
      [2] https://docs.podman.io/en/latest/markdown/podman-systemd.uni...
    • Công ty chúng tôi cũng dùng docker-compose.yml cho phát triển nên tôi hiểu nỗi đau này
      Nhìn chung nó hoạt động rất tốt, nhưng trên OSX đôi khi hành xử kỳ lạ vì cái VM trung gian chết tiệt đó
      Thỉnh thoảng tôi kiểm tra khả năng tương thích với Podman, nhưng giờ đã chấp nhận rằng triết lý rootless khác nhau quá nhiều nên không thể tương thích hoàn toàn
      Dù vậy công cụ này rất tuyệt cho production. Tôi đang chạy vài dự án cá nhân trên node bare-metal bằng các service systemd tự viết, hiện tại là containerd ở chế độ user
      Khi có thời gian có lẽ tôi sẽ thử chuyển sang nó
  • Bài viết này nói Podman-Compose “không được bảo trì tích cực” và chỉ ra commit cuối cùng là 5 tháng trước, rồi ngay sau đó lại đề xuất Podlet như một phương án thay thế dù commit cuối cùng của nó cũng là 5 tháng trước, khá thú vị
    Podlet có thể hữu ích, nhưng không hỗ trợ nhiều tính năng của Docker Compose và cũng không phải lúc nào cũng chuyển đổi sạch sẽ
    Đặc biệt, nó không hỗ trợ tính năng xếp chồng nhiều tệp YAML như -f docker-compose.yml -f docker-compose.override.yml

    • Nếu thích tệp compose, bạn có thể dùng ứng dụng compose của Docker[1] cùng với Podman[2]
      Có vẻ compose CLI điều khiển engine qua socket, và Podman với Docker engine có API gần như giống nhau
      Tôi đang dùng cách này vì podman-compose không hoạt động như mong đợi
      Docker-compose thường được cài như plugin của docker client, nhưng tôi dùng nó như một ứng dụng độc lập để chạy với Podman
      Ngoài ra, với thiết lập tích hợp, tôi thích dùng docker contexts hơn biến môi trường DOCKER_HOST
      Nếu chỉ Quadlet thuần túy chưa đủ mạnh, Quadlet[3] và Podman thông thường[4] cũng hỗ trợ chạy các manifest Kubernetes ở mức giới hạn
      Tôi vẫn chưa rõ Podman xử lý tùy chọn restart trong tệp compose như thế nào, vì Podman không có daemon giám sát
      Ngược lại, tôi biết tùy chọn healthcheck phụ thuộc vào systemd timer
      Khi dùng Podman trên Gentoo, một bản phân phối không dùng systemd, healthcheck tự động không hoạt động, nhưng nếu chạy healthcheck thủ công thì phần thiết lập còn lại vẫn chạy đến cuối
      [1] https://github.com/docker/compose
      [2] https://docs.podman.io/en/latest/markdown/podman-system-serv...
      [3] https://docs.podman.io/en/latest/markdown/podman-systemd.uni...
      [4] https://docs.podman.io/en/latest/markdown/podman-kube-play.1...
    • Công bằng mà nói, tính năng của Podlet không cần cập nhật liên tục, còn podman compose thì không như vậy
      Podlet là công cụ phụ trợ, và về lâu dài làm việc với Quadlet sẽ tốt hơn
    • Ngày commit không chính xác để chứng minh một thứ đang được bảo trì, nhưng có thể cho thấy nó không được bảo trì
      5 tháng không có nghĩa là không được bảo trì; phải khoảng 2 năm mới có thể nói vậy
      Dù thế, nó vẫn làm tăng lo ngại. Tuy nhiên, 5 tháng tuyệt đối không phải bằng chứng cho thấy đang được bảo trì
      Có nhiều lý do khiến một repository không được bảo trì vẫn được cập nhật, và chỉ có tính năng lưu trữ GitHub gần đây khá tệ mới ngăn được điều đó
  • Vài năm trước, khi làm lại cấu hình self-hosting từ đầu, tôi muốn thử thứ khác nên đã chọn openSUSE MicroOS, và cuối cùng chạy các container Podman dưới systemd/Quadlet; hiện tôi khá hài lòng với cấu hình này
    Container được tự động cập nhật bằng công cụ tích hợp của Podman, còn việc xem log và giám sát thì xử lý bằng các công cụ systemd quen thuộc
    Khi cần thay đổi gì đó, ngay cả khi quên vị trí tệp cấu hình, cũng dễ tìm, dễ đọc và dễ chỉnh sửa
    Rootless và daemonless cũng rất tốt
    Tôi đã thử vài thứ trong quá trình đó, nhưng podman compose cho cảm giác thô cứng; tôi mừng vì nó bị deprecated, và rõ ràng Quadlet mới là hướng đi
    Cần lưu ý là có đường cong học tập và tài liệu ít hơn Docker
    Nếu mục đích là bật/tắt stack nhiều dịch vụ trong phát triển cục bộ, tôi vẫn sẽ chọn Docker và docker compose

    • Tôi đang chạy Nextcloud bằng Fedora CoreOS trên một workstation cũ giá rẻ
      Mất một thời gian để chỉnh cấu hình cho đúng, nhưng tôi rất ấn tượng vì gần như không cần bảo trì. Đến giờ thì hoàn toàn chưa cần
      Ai muốn làm tương tự có thể tham khảo cấu hình của tôi
      https://github.com/jeppester/coreos-nextcloud
    • Tổ hợp Quadlet + MicroOS rất mạnh, và các bản phân phối atomic khác như Fedora CoreOS cũng vậy
      Tôi đang từ từ chuyển mọi node sang MicroOS, và ở công ty cũng đang thúc đẩy dùng MicroOS hoặc thứ tương tự
      Sự kết hợp giữa tự động rollback của OS nền với cấu hình container dạng khai báo và tự động cập nhật cho cảm giác rất ăn khớp
  • Định dạng này rõ ràng hơn podman generate systemd hay Kubernetes YAML, và tích hợp systemd cũng rất tuyệt
    Điều khó chịu là upstream của Podman không cung cấp repository cho Debian/Ubuntu
    Trên Debian stable, tôi bị kẹt ở 4.3.1 và bỏ lỡ nhiều tính năng mới, nên cuối cùng đã quyết định quay lại Docker compose

    • Tôi cũng gặp vấn đề tương tự khi cố dùng Quadlet trên Raspberry Pi chạy Debian
      Cách vòng tránh được đề xuất là tự compile Podman hoặc dùng debian/testing
  • Tôi thật sự hy vọng cách tiếp cận mới này sẽ giúp mọi người chuyển từ Docker sang Podman
    Docker-Compose là lý do khiến nhiều người do dự khi chuyển đổi, và tôi cũng từng như vậy
    Phải thừa nhận rằng trước Quadlet, Podman chưa có câu trả lời đúng nghĩa
    Nếu bạn từng ngần ngại rời Docker vì Docker-Compose, thì Podman kết hợp với Quadlet là một lựa chọn thay thế cạnh tranh hơn nhiều
    Có lẽ bạn sẽ không nhớ Docker nhiều như tưởng tượng, và còn có thêm lợi ích bảo mật nhờ chạy container rootless

    • Tôi sẽ chưa chuyển cho đến khi có thể có nhiều môi trường Quadlet độc lập
      Cách nhét mọi thứ vào ~/.config/quadlet/systemd là không ổn
      Điều này là bắt buộc cho phát triển và kiểm thử, còn hiện tại Quadlet thuần túy là một giải pháp triển khai
  • Tôi rất thích Quadlet vì nó cho phép dùng container như các dịch vụ hệ thống thông thường
    Tuy nhiên, trải nghiệm người dùng của container rootless không thật sự khớp với cách khái niệm hóa này
    Thông thường, dịch vụ hệ thống chạy dưới một system user trong phiên systemd của hệ thống, nhưng dịch vụ của container rootless lại nằm trong phiên systemd của người dùng thuộc system user đó
    Sẽ thật tốt nếu có thể chạy Quadlet rootless bên trong phiên hệ thống

    • Tôi tò mò liệu có khác biệt thực chất nào so với việc bật user-linger và chạy rootless thông qua systemd của người dùng không
      Tôi vẫn luôn làm như vậy
    • Tôi cũng mong có thể chạy Quadlet rootless bên trong phiên hệ thống
      Ngoài ra, sẽ rất tốt nếu có thể chạy Quadlet rootless với tùy chọn DynamicUser=
      DynamicUser= từng là một cách tuyệt vời để giới hạn quyền của dịch vụ hệ thống, nhưng hiện không thật sự hợp với Podman
  • Thật thú vị khi thấy Quadlet trên trang nhất Hacker News. Tôi nghĩ nó chưa được chú ý đúng mức
    Ygal và Valentin của dự án từng gửi một bài viết khách về cách chạy client đường hầm inlets. Nó tương tự Ngrok/Cloudflared, nhưng là cách tự host mà không có giới hạn kiểu SaaS
    https://inlets.dev/blog/2023/10/03/client-quadlet.html
    Họ dùng [kube] thay vì [container] nên có thể mang theo YAML Kubernetes chuẩn, khiến tính di động khá tốt

  • Với những ai quan tâm, tôi đã tạo một template Ansible cho thấy việc triển khai Quadlet gần đây dễ đến mức nào
    GH: https://github.com/Mati365/hetzner-podman-bunjs-deploy