【失敗談】Gitea + Cloud Run + Litestream構成でデプロイする
概要
あまりやる人はいないかと思いますが、軽量のセルフホスト版のGithub/Bitbucket/GitLabライクのGiteaをCloud Runにデプロイし、GiteaのビルドインSQLiteをLitestreamを使ってCloud Storageへレプリケーションできるのか?を試した記事になります。
結果的には上手くいかなかったのですが、何かの役に立てば。。
Giteaとは?
Giteaは、Gitリポジトリのホスティングからコードレビュー、課題管理、パッケージレジストリ、CI/CDまでを一つにまとめたMITライセンスのオープンソースDevOpsプラットフォームです。
Go言語で書かれているため動作が軽く、Raspberry Piでも動かせるほどの省リソースで、Linux・macOS・Windowsといった主要OSに簡単にセルフホストできます。
プロジェクトは2016年にGogsからフォークして生まれ、コミュニティドリブンで機能拡張を続けており、GitHub Actions互換の「Gitea Actions」などモダンな開発ワークフローも標準サポートしています。
“Git with a cup of tea”――お茶のように気楽に使えるGitサービスを目指すその思想は、個人開発者から大規模組織まで多様なユーザーに支持されています。
Litestreamとは?
Litestreamは、SQLiteのWALをリアルタイムにS3などへ転送し、単一サーバーでも秒単位RPOを実現するGo製ストリーミングレプリケーション&バックアップツールです。
YAML設定+ワンコマンドで導入でき、復旧は
litestream restoreでスナップショットと増分を自動再構成するだけ。MITライセンスのオープンソースとして2021年に公開され、BoltDB作者Ben Johnson氏が中心となりコミュニティ開発が継続中です。
Fly.ioなどの“サーバーサイドSQLite”事例を後押しし、Raspberry Piからクラウドまで軽量にスケールする点が支持されています。
ローカルでGiteaを動かす
まずはローカル環境でGiteaを動かしてみたいと思います。👇を参考に進めていきます。
まずはディレクトリを作成し、必要なディレクトリを作成します。
$ mkdir selfhost-gitea
$ cd selfhost-gitea
次に以下内容で compose.yml を作成します。
volumes:
gitea-data:
gitea-config:
services:
gitea:
image: docker.gitea.com/gitea:1.24.0-rootless
restart: always
working_dir: /usr/src
volumes:
- .:/usr/src
- gitea-data:/var/lib/gitea
- gitea-config:/etc/gitea
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:2222"
早速起動して確認してみます。
$ docker compose up -d
起動後、http://localhost:3000/ にアクセスして以下の画面が表示されればOKです。

ひとまずデフォルトの設定のまま「Giteaをインストール」を選択します。
その後アカウントを作成しログインすると👇の画面が表示されます。

ひとまずローカルでGiteaを動かすまで実施できました。簡単ですね ✨
この時の SQLite のファイルパスは先ほどの設定項目を見ると /var/lib/gitea/data/gitea.db にデフォルトでなっている事が分かります。
MinIOを使ってローカルでLitestreamを動かす
次にローカルで試せるMinIOを使ってLitestreamのレプリケーション先として設定して先ほどのSQLiteのdbファイルをレプリケーションさせてみたいと思います。
先ほどの compose.yml にMinIOの環境を追加します。
volumes:
gitea-data:
gitea-config:
minio_data: # 追加
services:
gitea:
# ...
# 👇 depends_on 追加
depends_on:
- minio
# 👇 service 追加
minio:
image: minio/minio:RELEASE.2025-02-18T16-25-55Z
volumes:
- minio_data:/minio/data
command: server --console-address ':9001' /minio/data
ports:
- 9000:9000
- 9001:9001
また services:gitea 内で Litestream とデバッグ用に SQliteコマンド を使いたいので👇の Dockerfile.local を用意してそちらを使うようにします。
build:
context: .
dockerfile: Dockerfile.local
FROM keinos/sqlite3:3.50.1 AS sqlite3-builder
FROM alpine AS litestream-builer
ARG LITESTREAM_VERSION=0.3.13
ENV LITESTREAM_VERSION=${LITESTREAM_VERSION}
ADD "https://github.com/benbjohnson/litestream/releases/download/v${LITESTREAM_VERSION}/litestream-v${LITESTREAM_VERSION}-linux-amd64.tar.gz" /tmp/litestream.tar.gz
RUN tar -C /usr/local/bin -xzf /tmp/litestream.tar.gz
FROM docker.gitea.com/gitea:1.24.0-rootless
COPY /usr/bin/sqlite3 /usr/bin/sqlite3
COPY /usr/local/bin/litestream /usr/local/bin/litestream
compose.yml
services:
gitea:
# 👇に変更
build:
context: .
dockerfile: Dockerfile.local
image: selfhost-gitea
container_name: "selfhost-gitea"
# ....
ここまできたら docker compose up -d で起動して、MinIOコンソール画面 http://localhost:9001 にアクセスします。
デフォルトだとユーザー/パスワード共に minioadmin なのでログインしときます。
まずはレプリケーション先のBucketを作成しときます。「Create Bucket」から「gitea-bucket」の名前でBucketを作成します。

次にサイドメニューの「Access Keys」>「Create access key +」から新しいAccessKeyを作成しときます。

次に litestream.yml を以下内容で作成します。 access-key-id と secret-access-key は先ほど作成したものを設定しときます。
dbs:
- path: /var/lib/gitea/data/gitea.db
replicas:
- type: s3
bucket: gitea-bucket
path: gitea.db
endpoint: http://minio:9000
region: us-east-1
access-key-id: xxxxxxxx
secret-access-key: xxxxxxx
force-path-style: true
早速Litestreamを動かしてみます。
litestream replicate -config ./litestream.yml
MinIOの先ほど作ったBucket内にオブジェクトが作成されています。

試しに何かリポジトリを作成しときます。

litestream を Ctrl + C で止めて以下を実施し gitea.db を確認してみます。
$ sqlite3 /var/lib/gitea/data/gitea.db
SQLite version 3.50.1 2025-06-06 14:52:32
Enter ".help" for usage hints.
sqlite> select * from repository;
2|1|slowhand|test|test|||0||main|main|...
先ほど作成したリポジトリが登録されています。これらを一旦削除してMinIOのBucketからリストアしてみます。
$ rm /var/lib/gitea/data/gitea.db
$ rm /var/lib/gitea/data/gitea.db-*
$ litestream restore -if-replica-exists -o /var/lib/gitea/data/gitea.db -config ./litestream.yml /var/lib/gitea/data/gitea.db
$ sqlite3 /var/lib/gitea/data/gitea.db
SQLite version 3.50.1 2025-06-06 14:52:32
Enter ".help" for usage hints.
sqlite> select * from repository;
2|1|slowhand|test|test|||0||main|main|...
ちゃんとリストアできてそうな雰囲気です。この状態でコンテナ再起動して http://localhost:3000 にアクセスして前回と変わらず表示できとけばOKです。
ローカルでは volumes で永続化が必要な箇所をまるっと実施している為、コンテナ再起動しても普通に使えます。それが Cloud Run だと状況が変わってきます。次は実際にうまくいかなかったCloud Runへのデプロイを試していきます。
デプロイ準備
Cloud Run へデプロイする為に、Cloud StorageのBucket作成やArtifact Registryにリポジトリを作成します。
Cloud StorageのBucket作成
今回は selfhost-gitea という名前でBucketを作成していきます。Bucket名は適宜設定して下さい。
gcloud storage buckets create selfhost-gitea \
--uniform-bucket-level-access \
--location=asia-northeast1 \
--project={PROJECT_ID} # プロジェクトIDを指定
またGiteaの設定ファイル app.ini をCloud Storageにボリュームマウントさせる為のBucketも作成しときます。理由は後述します。Bucket名は selfhost-gitea-config で作成しました。
--add-volume=name=gitea-config,type=cloud-storage,bucket=selfhost-gitea-config \
--add-volume-mount=volume=gitea-config,mount-path=/etc/gitea
gcloud storage buckets create selfhost-gitea-config \
--uniform-bucket-level-access \
--location=asia-northeast1 \
--project={PROJECT_ID} # プロジェクトIDを指定
Artifact Registryリポジトリ作成
今回は selfhost-gitea-repository という名前でリポジトリを作成しました。
gcloud artifacts repositories create selfhost-gitea-repository \
--repository-format=docker \
--location=asia-northeast1 \
--description=selfhost-gitea \
--async \
--disable-vulnerability-scanning \
--project={PROJECT_ID} # プロジェクトIDを指定
デプロイ用のDockerfile作成
次に実際にselfhost-gitea-repository へpushするDockerイメージ用のDockerfileを作成します。最終的に以下のようなDockerfileを作成しました。
FROM alpine AS litestream-builer
ARG LITESTREAM_VERSION=0.3.13
ENV LITESTREAM_VERSION=${LITESTREAM_VERSION}
ADD "https://github.com/benbjohnson/litestream/releases/download/v${LITESTREAM_VERSION}/litestream-v${LITESTREAM_VERSION}-linux-amd64.tar.gz" /tmp/litestream.tar.gz
RUN tar -C /usr/local/bin -xzf /tmp/litestream.tar.gz
FROM docker.gitea.com/gitea:1.24.0-rootless
ENV GCS_BUCKET_URL=gcs://selfhost-gitea/gitea.db
COPY /usr/local/bin/litestream /usr/local/bin/litestream
COPY docker-entrypoint.sh /
COPY litestream.yml /etc/litestream.yml
ENTRYPOINT ["/docker-entrypoint.sh"]
やっている事としては gitea のDockerイメージに litestream-builer のフェーズで準備したLitestreamのバイナリをコピーして使えるようにしてます。
docker-entrypoint.sh は以下になります。
#!/bin/bash
set -e
echo "starting restore"
echo "gcs url >> ${GCS_BUCKET_URL}"
if [ -f /var/lib/gitea/data/gitea.db ]; then
echo "Database already exists"
else
echo "Database does not exist. Creating..."
litestream restore -if-replica-exists -o /var/lib/gitea/data/gitea.db "${GCS_BUCKET_URL}"
fi
echo "done with restore. starting litestream"
exec litestream replicate \
-exec "/usr/local/bin/gitea -c ${GITEA_APP_INI:-/etc/gitea/app.ini} web --port ${PORT}"
最後のコマンドでは直接 gitea web コマンドを実行し、その際にportを指定するようにしています。
またCloud Storageへ向けた設定の litestream.yml は以下になります。
dbs:
- path: /var/lib/gitea/data/gitea.db
replicas:
- url: ${GCS_BUCKET_URL}
DockerイメージをPush
準備ができたので早速DockerイメージをビルドしてArtifact RegistryリポジトリへPushしていきます。 {PROJECT_ID} は適宜置き換えて下さい。
# ビルド
$ docker build -t asia-northeast1-docker.pkg.dev/{PROJECT_ID}/selfhost-gitea-repository/gitea --platform amd64 .
# 認証とPush
$ gcloud auth configure-docker asia-northeast1-docker.pkg.dev --project={PROJECT_ID}
$ docker push asia-northeast1-docker.pkg.dev/{PROJECT_ID}/selfhost-gitea-repository/gitea
Cloud Run デプロイ
最後にCloud Runへデプロイしていきます。
gcloud run deploy selfhost-gitea \
--image=asia-northeast1-docker.pkg.dev/{PROJECT_ID}/selfhost-gitea-repository/gitea \
--region=asia-northeast1 \
--allow-unauthenticated \
--project={PROJECT_ID} \
--port=8080 \
--add-volume=name=gitea-config,type=cloud-storage,bucket=selfhost-gitea-config \
--add-volume-mount=volume=gitea-config,mount-path=/etc/gitea
--max-instances=1 \
--min-instances=1
デプロイが無事完了したら発行されたURLへアクセスします。するとローカルでも設定した初期設定画面が表示されるかと思います。データベースのタイプを SQLite3 にして、必要あればサイトタイトルも変更しときます。

他はデフォルトのまま「Giteaをインストール」をクリックします。
「インストール中です、お待ちください…」から進まない!?

ここで何故か app.ini をCloud Storageにボリュームマウントしとかないと先に進まず /user/login が 404 でずっとアクセスできないという現象が起きました… 調べても原因が分からずだったのでご存じの方がいれば共有してもらえると助かります!
無事先に進むと以下の画面が表示され、あとは普通にGiteaを使えます。。。が、再デプロイを行うと永続化しとかないといけない箇所が足りてなくて、使える状態にはなりませんでした。。

試しにCloud Storage のボリューム マウントを/var/lib/giteaと/etc/giteaで使ってみる
ローカルで /var/lib/gitea と /etc/gitea をボリュームマウントしてた様に、Cloud Storageのボリュームマウントで同じようにやったらどうなるか? を試してみました。
selfhost-gitea-config と selfhost-gitea-data というBucketを作成し、Cloud Run デプロイ時にボリュームマウントを指定してみます。
gcloud beta run deploy selfhost-gitea \
--image=asia-northeast1-docker.pkg.dev/{PROJECT_ID}/selfhost-gitea-repository/gitea \
--region=asia-northeast1 \
--allow-unauthenticated \
--project={PROJECT_ID} \
--port=8080 \
--set-env-vars=DISABLE_SSH=true \
--set-env-vars=START_SSH_SERVER=false \
--max-instances=1 \
--min-instances=1 \
--add-volume=name=gitea-config,type=cloud-storage,bucket=selfhost-gitea-config \
--add-volume-mount=volume=gitea-config,mount-path=/etc/gitea \
--add-volume=name=gitea-data,type=cloud-storage,bucket=selfhost-gitea-data,mount-options="dir-mode=777;file-mode=666" \
--add-volume-mount=volume=gitea-data,mount-path=/var/lib/gitea
selfhost-gitea-data の方には mount-options で一応パーミッションを変更しています。その為コマンドが gcloud beta run になっています。
実際にデプロイしてみるとエラーになって落ちてしまいます。ログには以下のエラーが出力されていました。
code.gitea.io/gitea/modules/git.InitFull(ctx) failed: failed to set git global config gc.reflogexpire, err: exit status 4 - error: chmod on /var/lib/gitea/data/home/.gitconfig.lock failed: Operation not permitted
chmod を実行しようとして Operation not permitted で落ちているようです 👀
これは Cloud Storage FUSE がPOSIX に準拠しておらず以下の制限事項がある為失敗しているようです。
Cloud Storage FUSE は、ファイルのロックやファイルへのパッチ適用をサポートしていません。バージョン管理システムはファイルのロックとパッチ適用に依存しているため、バージョン管理システムのリポジトリを Cloud Storage FUSE のマウント ポイントに保存しないでください
という事で今回のユースケースだとボリュームマウントは使えない事が分かりました。
まとめ
自分のやり方がマズかっただけで、工夫すれば Cloud Run で Gitea使える様になるのかもしれませんが、色々やらないといけない事を考えると素直に別の方法でGitea運用するのが一番良さそうですw
Discussion