> ## Documentation Index
> Fetch the complete documentation index at: https://ayakaleaf-pro.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 自動更新 TeX Live Full 映像檔

> 使用 cron 更新 Overleaf 相容沙箱編譯器所使用的 TeX Live Docker 映像檔。

一般而言，每次執行 `bin/up` 時，[using-the-toolkit](/zh-TW/on-premises/installation/using-the-toolkit "mention") 都會自動更新我們的 TeX Live full 映像檔。為了避免手動更新，你可以使用下列指令碼自動更新 TeX Live 沙箱映像檔。將其新增為 cron 工作，並確認 cron 使用者有權限執行 `docker`。執行 `crontab -e`，並在檔案結尾加入下列這行。

```cron title="crontab -e" theme={null}
0 * * * * /usr/bin/flock -n /tmp/pull-texlive.lock /usr/local/bin/pull-texlive.sh
```

當前一次執行仍持有鎖定時，`flock -n` 會略過本次執行，因此緩慢的拉取作業不會不斷堆積。

<Tabs>
  <Tab title="直接連線">
    ```bash title="/usr/local/bin/pull-texlive.sh" theme={null}
    #!/usr/bin/env bash
    set -uo pipefail

    IMAGE="ghcr.io/ayaka-notes/texlive-full"
    TAGS=(2020.1 2021.1 2022.1 2023.1 2024.1 2025.1 2026.1)
    LOG=/var/log/pull-texlive.log

    exec >>"$LOG" 2>&1

    # Hours since the epoch, modulo the tag count: one tag per hour,
    # so all 7 tags are refreshed every 7 hours.
    idx=$(( $(date +%s) / 3600 % ${#TAGS[@]} ))
    tag="${TAGS[$idx]}"

    echo "===== $(date -Is) pulling $tag (idx=$idx) ====="

    if timeout 50m docker pull "$IMAGE:$tag"; then
      echo "OK   $tag"
    else
      echo "FAIL $tag (exit $?)"
    fi

    docker image prune -f
    docker system df | tail -n +2
    ```
  </Tab>

  <Tab title="使用鏡像站">
    ```bash title="/usr/local/bin/pull-texlive.sh" theme={null}
    #!/usr/bin/env bash
    set -uo pipefail

    MIRROR="ghcr.nju.edu.cn"
    CANONICAL="ghcr.io"
    REPO="ayaka-notes/texlive-full"
    TAGS=(2020.1 2021.1 2022.1 2023.1 2024.1 2025.1 2026.1)
    LOG=/var/log/pull-texlive.log

    exec >>"$LOG" 2>&1

    # Hours since the epoch, modulo the tag count: one tag per hour,
    # so all 7 tags are refreshed every 7 hours.
    idx=$(( $(date +%s) / 3600 % ${#TAGS[@]} ))
    tag="${TAGS[$idx]}"

    echo "===== $(date -Is) pulling $tag (idx=$idx) via $MIRROR ====="

    if timeout 50m docker pull "$MIRROR/$REPO:$tag"; then
      # Retag to the canonical name the compiler is configured with. This only
      # adds a second name for the same layers; nothing is copied.
      docker tag "$MIRROR/$REPO:$tag" "$CANONICAL/$REPO:$tag"
      echo "OK   $tag"
    else
      echo "FAIL $tag (exit $?)"
    fi

    docker image prune -f
    docker system df | tail -n +2
    ```
  </Tab>
</Tabs>

**為什麼每小時輪替，而不是一次拉取全部**：標籤是由系統時鐘推算而來（自 epoch 起的小時數對標籤數量取餘數），因此此工作是無狀態的——不需要記錄「上次做到哪裡」，在重新開機或錯過某個小時後，循環也會自行對齊。每次執行只處理一個標籤；若 registry 摘要未變更，拉取作業只會檢查 manifest（`Image is up to date`），幾秒內即可完成。實際更新時每小時最多下載一個映像檔，並以 `timeout 50m` 設定上限，而 `docker image prune -f` 會清除因標籤移動而成為孤立狀態的映像層。

若要檢查同步是否正常：

```bash theme={null}
tail -20 /var/log/pull-texlive.log     # each run ends with "OK <tag>" or "FAIL <tag>"
grep -c '^FAIL' /var/log/pull-texlive.log
```

當新的 TeX Live 版本發布時，將其標籤加入 `TAGS` 陣列即可——輪替週期會自動調整。主機關機期間的時段會被略過而不會排入佇列；受影響的標籤只會在下一輪時更新。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.