オープンソースのヘッドレス CMS 「Strapi」をさくらの VPS でセルフホストしてみる

カスタマイズ性が高い、オープンソースのヘッドレス CMS として人気の Strapi について、簡単にですが Strapi をさくらの VPS でセルフホストするための環境構築からインストール手順、基本的な設定と Astro での使用例までまとめてみました。

カスタマイズ性が高い、オープンソースのヘッドレス CMS として人気の Strapi ですが、仕事で使用するヘッドレス CMS の候補として少し勉強したかったので、さくらの VPS (Ubuntu 20.04) でセルフホストしてみました。その手順について簡単にまとめておきます (ちなみに、セルフホストしなくても Strapi Cloud という SaaS 版もあります)。

Strapi の動作環境

今回は、この記事を書いている時点における、Strapi の最新バージョンである v5.1.0 を前提に進めます。

まず、Strapi を利用する場合の推奨環境については GitHub リポジトリにまとまっていますが、下記のような感じです。

OS

OS 推奨 最低限
Ubuntu 24.04 LTS
Debian 11 LTS
RHEL 9 LTS
macOS 14 12
Windows Desktop 11 10
Windows Server 非サポート 非サポート

Node.js

Node.js に関しては、「Active LTS」、もしくは 「Maintenance LTS」 ステータスのバージョンのみサポートなので注意してください。要するに偶数番号リリースで、LTS ステータスに移行済みの Node.js を使用する必要があります (現在であれば、「v20.18.0 (LTS)」、もしくは 「v18.20.4 (LTS)」 がサポートされています)。

データベース

データベース 推奨 最低限
MySQL 8.0 8.0
MariaDB 11.2 10.3
PostgreSQL 16.0 14.0
SQLite 3 3

ちなみに、SQLite を使用する場合は、Python のインストールが必要です。

今回、私の環境では、MySQL を使用しています。

Strapi のインストール

冒頭にも書いたとおり、今回使用するサーバ OS は、Ubuntu 20.04 です。また、下記はすでにインストールされている前提で進めます。

  • node.js
  • npm (yarn など別のパッケージマネージャでもよい)
  • MySQL
  • nginx
  • certbot
  • PM2

Web サーバの準備

今回使用する Web サーバは、nginx です。

まず、Strapi をインストールするためのディレクトリを作ります。strapi.example.com というドメインで運用する前提です。

sudo mkdir -p /var/www/strapi.example.com/html
sudo chown -R www-data:www-data /var/www/strapi.example.com
sudo chmod -R 755 /var/www/strapi.example.com

strapi.example.com 用の設定ファイルを新規作成し、

sudo vim /etc/nginx/sites-available/strapi.example.com

とりあえず下記のように記述して保存しておきます。

server {
  listen 80;
  listen [::]:80;
  root /var/www/strapi.example.com/html;
  index index.html index.htm index.nginx-debian.html;
  server_name strapi.example.com;
  add_header Access-Control-Allow-Origin '*';
  location / {
    proxy_pass http://localhost:1337;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_hide_header 'Access-Control-Allow-Origin';
  }
}

作成した設定ファイルのシンボリックリンクを nginx が起動時に読み取る sites-enabled ディレクトリに作成することで設定が有効になります。

sudo ln -s /etc/nginx/sites-available/strapi.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

ついでに、Let's Encrypt でサーバ証明書を取得し、HTTPS で接続可能にしましょう。certbot を使います。

sudo certbot --nginx -d strapi.example.com

質問に答えていくだけなので簡単。設定ファイルも certbot に書き換えてもらいましょう。

自動更新が有効になっているか確認して完了です。

sudo systemctl status certbot.timer

最後に nginx を再起動。

sudo nginx -t
sudo systemctl reload nginx

MySQL の準備

MySQL にログインし、ユーザーと Strapi で使用するデータベース (今回は 「strapi」 という名前で) を作成します。当然ながら、すでにユーザーが設定済みの場合はデータベースの作成だけで問題ありません。

mysql> CREATE USER '<username>'@'localhost' IDENTIFIED BY '<password>';
mysql> CREATE DATABASE strapi;
mysql> GRANT ALL ON strapi.* TO <username>@localhost;
mysql> FLUSH PRIVILEGES;
mysql> EXIT;

今作ったユーザーでログインできることを確認し、

mysql -u <username> -p

データベースも問題なく作成されていることを一応確認します。

mysql> SHOW DATABASES;

文字コードなどはデフォルトのままで問題ないと思いますが、こちらも一応確認して必要なら設定を変更します。

mysql> STATUS;

Strapi 本体のインストール

まず、先ほど作成した Strapi インストールディレクトリに移動します。

cd /var/www/strapi.example.com/html

インストールには npx コマンドが使用できますので、あとはウィザード形式で質問に答えていくだけです。

sudo npx create-strapi@latest

最初に 「プロジェクト名どうする?」 と聞かれますので、今回は strapi と入力して Enter します。これで、Strapi は /var/www/strapi.example.com/html/strapi にインストールされることになります。

次に、「Strapi Cloud に Login/Signup するか?」 っていう質問が来るので、ここは 「Skip」 を選択しましょう。

続けて、「データベースはデフォルトのを使う?」 って聞かれるので、ここも 「No」 を選択し、次に出てくるデータベースの選択肢で 「MySQL」 を選択します。

あとは聞かれた事に答えれば終わると思います。参考までに下記が私が選択した結果 (途中で MySQL のユーザー名やパスワードを聞かれているのもわかると思います)。

? Please log in or sign up. Skip
? Do you want to use the default database (sqlite) ? No
? Choose your default database client mysql
? Database name: strapi
? Host: localhost
? Port: 3306
? Username: username
? Password: ***************************
? Enable SSL connection: No
? Start with an example structure & data? No
? Start with Typescript? Yes
? Install dependencies with npm? Yes
? Initialize a git repository? No

質問に答え終わるとインストールが進んでいくのであとは終わるまで待ちましょう。途中でエラーが出なければ成功です。

インストールされたディレクトリに移動します。

cd strapi

公開 URL の設定

インストールディレクトリ直下に、.env ファイルがありますのでエディタで開きます。

sudo vim .env

.env ファイルには、インストールウィザードで質問されたことなども自動的にここに反映されていますので、内容を確認しつつ、下記の記述を追加して上書きします。

# Server
HOST=localhost
PORT=1337
PUBLIC_URL=https://strapi.example.com //←これ追記

...以下略...

次に、config/server.ts を編集します。

sudo vim config/server.ts

config/server.ts の内容は下記のようになっていますが、url オプションを追加して、こちらも上書き保存しておきましょう。

export default ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: env('PUBLIC_URL'), //←これ追記
  app: {
    keys: env.array('APP_KEYS'),
  },
});

管理画面の日本語化

これは必要ない人はやらなくてもよいですが、一応、管理画面を日本語化できます。とはいっても、すべてのパーツに日本語訳が当たっているわけではないので、一部が日本語になるだけの中途半端なものですから、気休め程度に思ってください。

インストールディレクトリ内の、src/admin に、app.example.tsx というファイルがありますので、これを app.tsx にリネームします。その後、その app.tsx を編集しましょう。

sudo cp src/admin/app.example.tsx src/admin/app.tsx
sudo vim src/admin/app.tsx

下記のような内容になっていて、初期状態だと locales の部分がすべてコメントアウトされていますが、この中から 'ja' のコメントアウトを外して上書き保存します。

import type { StrapiApp } from '@strapi/strapi/admin';

export default {
  config: {
    locales: [
      // 'ar',
      // 'fr',
      // 'cs',
      // 'de',
      // 'dk',
      // 'es',
      // 'he',
      // 'id',
      // 'it',
      'ja',
      // 'ko',
      // 'ms',
      // 'nl',
      // 'no',
      // 'pl',
      // 'pt-BR',
      // 'pt',
      // 'ru',
      // 'sk',
      // 'sv',
      // 'th',
      // 'tr',
      // 'uk',
      // 'vi',
      // 'zh-Hans',
      // 'zh',
    ],
  },
  bootstrap(app: StrapiApp) {
    console.log(app);
  },
};

Strapi の起動と設定

自分が、Strapi のインストールディレクトリ (/var/www/strapi.example.com/html/strapi) にいることを確認したら、PM2 を使用して Strapi を起動します。

sudo pm2 start npm --name strapi -- run develop

PM2 のログを確認して、問題なく Strapi が起動したことを確認します。

sudo pm2 logs strapi --lines 50

ブラウザから設定

問題なさそうなら、https://strapi.example.com にブラウザでアクセスすると、アカウント作成画面になると思いますので、アカウントを作ってログインします。

Strapi アカウント作成画面

最初に、「Content-Type Builder」 を使用して、コンテンツタイプを作りましょう。今回はサンプルとして 「Article」 という記事更新用のコンテンツタイプを作りました。

Strapi コンテンツタイプ設定画面

ここで設定している 「API ID (Singular)」 と 「API ID (Plural)」 は後ほど API でデータを取得する際に使いますのでわかりやすいものを設定しましょう。

コンテンツタイプを作ったら、適当に記事を登録したりして試してみるとよいでしょう。

次に、投稿された記事データを API で取得できるようにします。設定によっては認証不要で (API のエンドポイントを知っている人なら) 誰でも API からデータを取得可能にすることもできますが、通常の運用でそんなことはしないと思いますので、API 用のアクセストークンを発行して、認証に使用できるようにします。

メインメニューの 「API Tokens」 を選択したら、適当な名前を付けて保存します。アクセストークンは 1 度しか表示されないので確実に保存しましょう。

API から記事データを読み取るだけであれば、「Token Type」 を 「Read-only (読み取り専用)」 にしておきます。

Strapi API トークンの発行

画面下部に先ほど作ったコンテンツタイプ 「Article」 が表示されていると思いますが、「find」 と 「findOne」 権限が有効になっていれば、データは取得できます。

アクセストークンを保存したら、次は権限設定を行います。

メインメニューの 「Roles」 を選択したら、「Authenticated (認証済みアカウント)」 の権限設定に進みましょう。

Strapi ロール設定画面

コンテンツタイプ 「Article」 の権限を開き、「find」 と 「findOne」 権限を有効にします。

strapi-04-role.png

これで、API から 「Article」 コンテンツタイプのデータを取得可能になります。

あと、管理画面の言語設定については、アカウントの設定画面から行えます。必要なら変更しておきましょう。

Strapi アカウント設定画面

Strapi のアップデート

稼働中の Strapi を最新版にアップデートしたい場合も、npx コマンドが使用できますので、それに任せましょう。基本的に最新版にアップデートするならインストールディレクトリに移動後、下記のコマンドを実行すれば OK です。

sudo npx @strapi/upgrade latest

詳しいドキュメントは下記にあります。

Strapi API を Astro で使ってみる

Astro で、Strapi API からデータを取得して、ページをレンダリングする方法を簡単に試してみます。Astro のインストール手順などは省きます。

まず、src/libStrapi.ts を作成します。内容は下記のような感じで、Strapi API からデータを取得するための処理を記述します。

// src/libStrapi.ts

import type Article from "../interfaces/article";

export const fetchApi = async (): Promise<Article> => {
    const url = import.meta.env.PUBLIC_STRAPI_URL;
    const authToken = import.meta.env.STRAPI_API_KEY;

    const key = {
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${authToken}`
        },
    }
    const res = await fetch(`${url}/articles`, key).catch((err) => {
        console.error(err);
        throw new Error("Failed to fetch data");
    });
    if (!res) {
        throw new Error("No response from fetch");
    }
    const json = await res.json();
    return json;
}

事前に .env ファイルを作成し、環境変数、PUBLIC_STRAPI_URLSTRAPI_API_KEY を設定しておきましょう。PUBLIC_STRAPI_URL には、API の ベース URL (今回の例だと https://strapi.example.com/api) を、STRAPI_API_KEY には、管理画面から取得した API のアクセストークンを指定します。

Strapi の API に関するリファレンスは下記にあります。

エンドポイントとしては、/api/:pluralApiId へのリクエストで指定したコンテンツの一覧を取得できます。:pluralApiId は、コンテンツタイプを作成するときに設定した、「API ID (Plural)」 の値ですね。

なので、今回のサンプルソースだと、https://strapi.example.com/api/articles をエンドポイントとして指定しています。

なお、読み込んでいる型定義、src/interfaces/article.ts の中身は下記のような感じです。ここは、作ったコンテンツタイプの設定によります。

titledescriptioncontentslug の各項目は、私が 「Article」 コンテンツタイプを更新するために設定した各フィールドですが、あくまで例です。

// src/interfaces/article.ts

export default interface Article {
    data: {
        id: number;
        title: string;
        description: string;
        content: string;
        slug: string;
        createdAt: string;
        updatedAt: string;
        publishedAt: string;
    }[];
    meta: {
        pagination: {
            page: number;
            pageSize: number;
            pageCount: number;
            total: number;
        };
    };
}

あとは、src/pages 内で読み込んで使えば終わり。例えば、かなり雑ですが、下記のように。

// src/pages/index.astro

---
import Layout from "../layouts/Layout.astro";
import { fetchApi } from "../lib/Strapi";

const json = await fetchApi();
const articles = json.data;
---

<Layout title="Strapi Test">
  <main>
    <ul>
      {
        articles.map((article) => (
          <li>
            <a href={`/articles/${article.slug}/`}>{article.title}</a>
          </li>
        ))
      }
    </ul>
  </main>
</Layout>

実際の運用においては、Webhook の設定をはじめ、コンテンツの更新にあわせたビルド・デプロイの仕組みを用意しなければなりませんが、GitHub Actions、あるいは VarcelNetlify などを利用すれば比較的手軽に環境構築は可能です。


ということで、簡単にですが Strapi をセルフホストするための環境構築からインストール手順、基本的な設定と Astro での使用例までまとめてみました。

今の時代、なるべくヘッドレスな CMS を使用して、Web サイトの構築や更新を Git ワークフロー内で完結したいわけですけども、Strapi はひとつの選択肢として使いやすいと思いますので、個人的にはオススメです。


関連エントリー

記事をここまで御覧頂きありがとうございます。
この記事が気に入ったらサポートしてみませんか?