Goでアプリを書く最大の運用上の利点は,「1つの実行ファイル(シングルバイナリ)」にコンパイルできることだ.ランタイムも依存ライブラリも要らず,バイナリを置いて動かすだけ── デプロイが圧倒的に簡単になる.

PythonやNodeのようにランタイムや大量の依存を本番サーバーに入れる必要がない.ビルドした1ファイルをVPSに送り,systemdで動かすだけ.これがGoのデプロイの強みだ.

この記事は,Go製アプリをVPSで公開する個人開発者に向けている.静的バイナリのビルド,クロスコンパイル,systemd常駐,Nginx連携,極小Dockerイメージまでを解説する.

読み終えたとき,あなたはGoの軽量・高速なデプロイの強みをフルに活かし,最小構成でアプリを公開できるようになっている.

なぜGoのデプロイは簡単なのか

Goはコンパイル時にすべてを1つの実行ファイルに静的リンクする.ランタイム・依存ライブラリ・標準ライブラリがバイナリに含まれるため,本番サーバーには何もインストールしなくてよい.

これは運用上,絶大な利点だ.「ランタイムのバージョン違い」「依存のインストール失敗」「環境差異」といった,他言語で悩まされる問題がほぼ消える.バイナリが動けば,どこでも同じように動く.

さらにGoのバイナリは起動が速く,メモリ効率が良く,並行処理に強い.小さなVPSでも高い性能を発揮でき,リソースを節約できる.

「ビルドして,バイナリを置いて,動かす」── このシンプルさが,Goをデプロイ観点で非常に魅力的にしている.

静的バイナリをビルドする

Goのデプロイの第一歩は,依存のない静的バイナリをビルドすることだ.通常のビルドでほぼ静的になるが,完全に静的にするオプションも知っておくとよい.

本番用バイナリのビルド

# 基本のビルド
go build -o myapp

# 完全に静的リンク(CGO無効化)+サイズ削減
CGO_ENABLED=0 go build -ldflags="-s -w" -o myapp
# -s -w でデバッグ情報を削りバイナリを小さくする

CGO_ENABLED=0C依存を排した完全な静的バイナリになり,どんなLinux環境でも動く(極小Dockerイメージにも最適).-ldflags="-s -w"はデバッグ情報を削ってバイナリサイズを縮める.これで「1ファイルを置けば動く」状態になる.

クロスコンパイル ― 手元でVPS向けにビルド

Goはクロスコンパイルが得意だ.手元のMac/WindowsからでもVPS(Linux)向けのバイナリを作れる.VPS上でビルドする必要すらない.

Linux向けにクロスコンパイル

# 手元のどのOSからでもLinux/amd64向けバイナリを生成
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o myapp-linux

# VPSに転送(scp)
scp myapp-linux myvps:/home/deploy/app/myapp

GOOS=linux GOARCH=amd64Linux向けバイナリを手元で生成し,scpでVPSに送るだけ(SSH記事参照).VPSにGoをインストールする必要すらない.ARM系VPSならGOARCH=arm64にする.この手軽さはGoならではだ.

systemdで常駐させる ― 最小の本番構成

送ったバイナリをsystemdサービスとして常駐させれば,本番運用の最小構成が完成する(systemdの記事参照).自動再起動もOS再起動への対応も,systemdが担う.

/etc/systemd/system/myapp.service

[Unit]
Description=My Go App
After=network.target
[Service]
User=deploy
WorkingDirectory=/home/deploy/app
EnvironmentFile=/home/deploy/app/.env
ExecStart=/home/deploy/app/myapp
Restart=always
[Install]
WantedBy=multi-user.target

これだけだ.ランタイムも依存も要らず,バイナリ1つをsystemdで動かす──これがGoの本番構成の完成形だ.Node(PM2)やPython(Gunicorn)のような専用のプロセスマネージャやワーカー設定すら不要なことが多い.Goのバイナリ自体が効率的な並行処理をこなすからだ.

Nginxとの連携 ― HTTPS終端と静的配信

Goアプリも,前段にNginx(またはCaddy)を置いてHTTPS終端させるのが基本だ(リバースプロキシ記事参照).Goは標準ライブラリだけで高性能なHTTPサーバーになるが,TLS終端やドメイン管理は前段に任せると運用が楽だ.

NginxからGoアプリへ振り分け

server {
    listen 443 ssl;
    server_name example.com;
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Goアプリは127.0.0.1:8080でlocalhostバインドし,Nginxが前段でHTTPSを終端する.Goの性能を活かしつつ,証明書管理やドメイン振り分けはNginx/Caddyに任せる── この分担が運用を楽にする.Caddy(前記事)と組み合わせれば自動HTTPSも手軽だ.

極小Dockerイメージ ― scratchで数MB

Goの静的バイナリは,Dockerでも極小イメージを作れる.マルチステージビルドで,本番ステージにバイナリ1つだけを置けば,数MBのイメージになる(イメージ最適化記事参照).

極小Dockerイメージ(マルチステージ)

FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /myapp

# 本番ステージ: ほぼ空のベースにバイナリだけ
FROM gcr.io/distroless/static
COPY --from=builder /myapp /myapp
ENTRYPOINT ["/myapp"]

本番ステージにdistrolessやscratch(ほぼ空)+バイナリ1つだけを置くことで,イメージが数MB〜十数MBになる.攻撃面も極小だ(OSパッケージがほぼ無いため脆弱性も少ない).Goのシングルバイナリの強みが,Dockerでも最大限に活きる.

デプロイ更新 ― バイナリ差し替えの手軽さ

Goのデプロイ更新は,新しいバイナリに差し替えてサービスを再起動するだけだ.CI/CDと組み合わせれば,ビルド→転送→再起動を自動化できる(CI/CD記事参照).

更新の基本手順

# 手元/CIでビルド
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o myapp-new
# 転送して差し替え,再起動
scp myapp-new myvps:/home/deploy/app/myapp
ssh myvps 'sudo systemctl restart myapp'

バイナリを差し替えてsystemctl restartするだけ── 依存のインストールもビルドもVPS上で不要なので,更新が極めて速い.より無停止に近づけたいなら,前段プロキシでの切り替えやソケットの引き継ぎ(graceful restart)も可能だが,個人開発では再起動の一瞬のダウンは許容できることが多い.更新前のスナップショットがあれば,問題時もすぐ戻せる.

補論:Goの軽量さは「小さなVPS」でも高性能を引き出す

Goのバイナリはメモリ効率が良く起動も速いため,小さなVPSでも高い性能を発揮する.依存もランタイムも不要なので,VPSを汚さず軽量に保てる.

高速NVMe・50種類以上のOSテンプレートに対応した国内VPS─シン・VPS─ はNVMe SSDでバイナリの転送・起動が一瞬で,Goの軽量さと相まって小さなプランでも快適に本番運用できる.スナップショットでバイナリ差し替えの前に退避すれば,更新も安全だ.「依存のない静的バイナリ+固定費の速いVPS」は,コスパの面でも理想的な組み合わせだ.

公開には独自ドメイン+HTTPSを.取り扱い400種類以上のドメイン取得サービス─ムームードメイン─ でドメインを取得し,Nginx/Caddyで終端してGoアプリに振り分ければ,軽量で高速な本番サービスが最小コストで完成する.

よくある質問(FAQ)

Q1.Goのデプロイが簡単なのはなぜ?

1つの静的バイナリにコンパイルでき,ランタイムも依存も不要だからだ.バイナリを置いてsystemdで動かすだけ.「環境差異」「依存インストール失敗」といった他言語の悩みがほぼ無い.

Q2.VPS上でビルドすべき?手元でビルドすべき?

クロスコンパイルで手元(やCI)でビルドし,バイナリを転送するのが手軽だ(GOOS=linux).VPSにGoを入れる必要すらない.CI/CDでビルド→転送→再起動を自動化するのが理想.

Q3.PM2やGunicornのようなものは要る?

多くの場合不要だ.Goのバイナリ自体が効率的に並行処理する.systemdで常駐させ自動再起動させれば十分なことが多い.プロセス管理はsystemdに任せる.

Q4.CGO_ENABLED=0とは?

C依存を排して完全な静的バイナリにするオプションだ.これでどんなLinux環境でも動き,scratch/distrolessの極小Dockerイメージにも入れられる.特別なC依存がなければ付けてよい.

Q5.Dockerイメージはどれくらい小さくなる?

マルチステージビルド+distroless/scratchで数MB〜十数MBになる.含むものがバイナリだけなので,攻撃面も脆弱性も極小だ.Goの強みが最も出る部分の一つ.

Q6.無停止更新はできる?

graceful restartやソケット引き継ぎで可能だが,個人開発では再起動の一瞬のダウンは許容できることが多い.バイナリ差し替え+restartのシンプル手順+更新前スナップショットで十分実用的だ.

まとめ ― シングルバイナリが最小構成のデプロイを生む

Goのデプロイは,依存のない静的バイナリを,クロスコンパイルで作り,systemdで常駐させ,Nginx/Caddyで終端するだけだ.ランタイムも依存も要らない軽量さが,最小構成の本番運用を可能にする.

今日やるべきことは,CGO_ENABLED=0 GOOS=linux go buildでバイナリを作り,VPSに置いてsystemdで動かすこと.デプロイの手軽さに驚くはずだ.

軽量・高速・依存なし── Goの強みは,小さなVPSでこそ輝く.固定費の速いVPSと組み合わせ,最小コストで高性能なサービスを公開しよう.