コンテンツにスキップ
タイプアンロック
ステージ本番環境
難易度シンプル
ジョブタスクを自動化
対象ソロ起業家
対象バックエンド開発者
サービスCron
サービスファイル
Hoody の利点HTTP ネイティブ
タイプアンロック
ステージ本番環境
難易度シンプル
ジョブタスクを自動化
対象ソロ起業家
対象バックエンド開発者
サービスCron
サービスファイル
Hoody の利点HTTP ネイティブ
タイプアンロック
ステージ本番環境
難易度シンプル
ジョブタスクを自動化
対象ソロ起業家
対象バックエンド開発者
サービスCron
サービスファイル
Hoody の利点HTTP ネイティブ
タイプアンロック
ステージ本番環境
難易度シンプル
ジョブタスクを自動化
対象ソロ起業家
対象バックエンド開発者
サービスCron
サービスファイル
Hoody の利点HTTP ネイティブ
CRON · FILES · SELF-RETIRING

自分自身の引退をスケジュールするクリーンアップジョブ

クリーンアップを始めた日にマネージド cron エントリをプロビジョンします。expires_at は最後の予定実行を少し過ぎた日に設定。スクリプトは夜ごとに作業を切り分けて進め、何も残らなくなったら自分自身を DELETE します。カレンダーリマインダーも、ゾンビ crontab も、年に一度の「クリーンアップのクリーンアップ」レビューも要りません。

Cron ドキュメント
cleanup-stale-uploads.sh
# tail of cleanup-stale-uploads.sh
removed=$(find /uploads -mtime +90 -delete -print | wc -l)
echo "$removed files removed"

# self-aware tail: nothing left → retire
if [ -z "$(ls /uploads)" ]; then
  curl -fsS -X DELETE \
    "$CRON_URL/entries/$ENTRY_ID"
fi

自己認識する末尾。cron エントリは、自身のスクリプトの最後の 1 行です。

構造的に有限の寿命を持つクリーンアップ

3 つの離散的なフェーズ。それぞれの遷移は機械的で、人間が思い出す必要はありません。エントリは仕事がいつ終わるか、自分のカレンダー枠がいつ切れるかを知っています。

ライフサイクル · スケジュール → 分割 → 自己削除1 つのエントリ、有限の寿命
1 · SCHEDULE

クリーンアップを始める日にエントリを 1 つ POST

スケジュールは @daily、コマンドはスライサー スクリプト、expires_at は最後の予定実行の少し後に設定。締め切りはエントリの中にあります — Notion ドキュメントにも、Slack スレッドにもありません。

2 · SLICE

スクリプトは夜ごとに、優しく削っていく

毎回の実行で古いデータの一部を削除し、データベースを叩き潰さないようにします。1 日目は 247 件、6 日目は 1 件かもしれません。ペースは実際に残っている量で決まります。

3 · SELF-DELETE

仕事が終わったら、エントリは引退する

スクリプトの最後のブロックは、対象が空かを確認します。空なら DELETE /entries/[self] を撃ちます。万が一それが起きなくても、expires_at が数日後に安全網を発動します。

2 つの独立したトリガー — スクリプト自身のチェックと API の expires_at — が同じ結果に収束します: 目的より長く生き残らない crontab 行。

HTTP 呼び出し 2 つ、有限のクリーンアップ 1 つ

Hoody Cron はシステム crontab を JSON-CRUD でラップしたものです。POST がエントリを作成し、DELETE が削除し、expires_at が安全網です。夜ごとに走るスクリプトこそ、いつ終わったかを知っている張本人 — だから DELETE を呼ぶのもそのスクリプトです。

create-entry.sh
DAY 0 · POST
# day 0 — provision the cleanup
curl -X POST \
  https://cron.containers.hoody.com/users/me/entries \
  -H "Content-Type: application/json" \
  -d '["schedule":"@daily","command":"/srv/jobs/cleanup-stale-uploads.sh","expires_at":"2026-05-05T00:00:00Z"]'

# response
HTTP/1.1 201 Created
{ "id":"f3a1", "expires_at":"2026-05-05T00:00:00Z", "enabled":true }
cleanup-stale-uploads.sh
DAY 7 · DELETE
# inside the cron command itself
if [ -z "$(ls /uploads)" ]; then
  curl -X DELETE \
    "$CRON_URL/entries/$ENTRY_ID"
fi

# response
HTTP/1.1 204 No Content
# entry f3a1 was here. f3a1 deleted itself.

$ENTRY_ID は POST が返した UUID。コマンドライン / argv で渡すことでスクリプトがファイルから読み込み可能。いずれの方法でも cron エントリが自身を削除。

自己引退するクリーンアップが解き放つもの

重要なのは削除そのものではありません。3 か月後に誰もこれの存在を覚えていなくていい、ということです。

PRESSURE-FREE PACING

一気にではなく、スライスごとに

@daily は 24 時間ごとに走ります。スクリプトは古いデータのスライス — ファイル数千、行数千 — を削除して終了します。データベースは静かなまま、負荷曲線はまるで何も起きていないようです。

NO ZOMBIE CRONTAB

行はエンジニアより長生きしない

expires_at は JSON としてエントリに含まれます。発火するとシステム crontab から行が消えます。3 代後のエンジニアが 200 行を遡って cleanup-stale-uploads-v3 が今も何をしているのか首をひねる、ということはありません。

TWO TRIGGERS, ONE OUTCOME

セルフ DELETE と expires_at の安全網

スクリプトは仕事が終わった夜に自身を DELETE します。バグでそのパスが飛ばされても、expires_at が数日後にエントリを引退させます。独立した 2 つの仕組みのうち、どちらかは必ず発火します。

これがどうスケールするか

各マネージドエントリは API がシステム crontab に注入する JSON の 1 行です。スケールは Hoody ではなく cron 自身が保持できる量で決まります。

  1. RUN CADENCE1 / day

    @daily が定番のクリーンアップ リズム。もっと頻繁に走らせたければ、5 フィールド式で * * * * * — 分単位の解像度まで降りられます。

  2. ENTRY LIFETIMEexpires_at

    エントリ上の ISO-8601 タイムスタンプ。期限を過ぎると、API は次のスイープでその行を削除します。クリーンアップは自分の締め切りを越えて居座りません。

  3. TEARDOWN HTTPDELETE · 204

    実行中のコマンドの内側から DELETE /users/[user]/entries/[id] が動くのは、cron デーモンが自分の crontab をロックしないからです。API が変更を安全にスイープします。

標準の 5 フィールド cron 式とマクロ(@hourly、@daily、@weekly、@monthly、@yearly)。ユーザー単位の分離。各システムユーザーが自分の crontab を持ちます。Hoody Kit の Cron ページは、マネージドエントリと、必要なら従来形の生 crontab アクセスの両方をドキュメント化しています。

クリーンアップは、片付ける対象が消えるまで毎晩走る。

BEFORE · カレンダーリマインダー、ゾンビ cron 行AFTER · いつ終わったかを知る cron エントリ
変更前// TODO: delete this when /uploads is empty (2026?)未来の自分が一度だけ読み、その後二度と読まないコメント。
DELETE /entries/$ENTRY_ID · expires_at: 2026-05-05スクリプトから発火するティアダウンと、エントリ自体に埋め込まれた締め切り。
Cron API リファレンス

これが置き換えるもの

クリーンアップタスクが自然に消え去ることを期待される場面で、これらのパターンを置き換えます。

  • ゾンビ クリーンアップ cron ジョブ2022 年からの行で、誰も削除する勇気が出ない
  • // TODO: delete this when X is empty永続化してしまったコメント
  • 手作業の janitor スクリプトチームメイトが思い出したときに四半期ごとに走らせる
  • 自分を消さない Kubernetes CronJobttlSecondsAfterFinished はスケジュールではなく Pod に付く
  • 自前のライフサイクルマネージャー他のサービスを監視するためだけの内製サービス
  • Terraform で管理するライフサイクル ポリシーplan、apply、6 か月後にも tfstate に残っている

クリーンアップをプロビジョンし、引退日を設定し、立ち去りましょう。

Cron ドキュメントを読む

他のユースケースを読む