- 設計
設定値の管理設計|環境変数・設定ファイル・DBの使い分けと優先順位
設定値の管理設計とは、アプリケーションの動きを変える値を「どこに置き、どの順で採用し、いつ反映するか」を決めることです。 置き場所は、値の性質(環境で変わるか、秘密か、稼働中に変えるか)で決まります。
この記事では、置き場所の判定表、設定項目一覧のテンプレートと記入例、優先順位を解決するコードと実行結果、起動時の検証、似た概念との違いをまとめます。.env ファイルの書き方やサーバーの設定項目の一覧ではなく、置き場所と優先順位の設計 に絞ります。
確認環境(確認日 2026-10-06):Ubuntu 24.04、Python 3.11.15、OpenJDK 21.0.10、PostgreSQL 16.15。Spring Boot の内容は、今回開いた公式リファレンス(Spring Boot 4.1.1「Externalized Configuration」)の記載にもとづきます。
この記事の結論
- 環境で変わる値は環境変数(接続先のホスト名、ポート、ログレベル)。
- 構造を持つ既定値は設定ファイル(リポジトリに入れ、レビューを通す)。
- 稼働中に変える業務値は DB(上限件数、表示の切り替え。変更者と日時を残す)。
- 秘密情報はシークレット管理に保管し、起動時に環境変数として渡す(設定ファイルにも DB にも書かない)。
- 採用する順は 引数 > 環境変数 > 設定ファイル > 既定値。必須の値が無ければ 起動時に止める。
設定値の置き場所の判定表
| 置き場所 | 向いている値 | 例 | 変更の反映 | 履歴の残り方 | 向かない値 |
|---|---|---|---|---|---|
| コード内の既定値 | どの環境でも同じで、無くても動く初期値 | ページあたり件数 50、タイムアウト 30秒 | デプロイ | Git | 環境ごとに違う値、秘密情報 |
| 設定ファイル | 階層や一覧などの構造を持つ既定値 | ログの出力設定、機能ごとの上限のまとまり | デプロイ(再起動) | Git(レビューつき) | 秘密情報 |
| 環境変数 | 環境ごとに変わる値、起動時に渡す秘密情報 | DB のホスト名、ログレベル、API キー | 再起動 | 基盤側の設定履歴 | 入れ子の構造、長い一覧 |
| 起動引数 | その回の実行だけ変えたい値 | バッチの対象日、検証用のポート | その実行のみ | 実行ログ | 秘密情報(プロセス一覧に見える) |
| DB(設定テーブル) | 稼働中に担当者が変える業務値 | 予約できる日数の上限、お知らせ表示 | 即時〜数分(読み直しの方式による) | 更新者・更新日時の列、監査ログ | DB の接続先そのもの、秘密情報 |
| シークレット管理 | パスワード、API キー、署名鍵 | DB パスワード、外部 API のトークン | 再起動またはローテーション時 | 保管側のアクセス記録 | 秘密でない値(探しにくくなる) |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「設定値の管理設計|環境変数・設定ファイル・DBの使い分けと優先順位」
置き場所を決める4つの質問
上から順に答え、最初に「はい」になったところが置き場所です。
| 順 | 質問 | 「はい」のとき |
|---|---|---|
| 1 | 漏れると困る値か | シークレット管理に保管し、環境変数として渡す |
| 2 | 再起動なしで、運用担当者が変える値か | DB(設定テーブル) |
| 3 | 環境(開発・検証・本番)で値が変わるか | 環境変数 |
| 4 | 階層や一覧の構造を持つか | 設定ファイル |
| — | どれにも当てはまらない | コード内の既定値、または定数 |
The Twelve-Factor App は、設定を「デプロイごとに変わりうるものすべて」と定義し、コードから厳密に分離して環境変数に置く方針を示しています。目安として、認証情報を漏らさずにコードをいつでも公開できる状態か を確かめる方法も挙げられています。この記事の判定表は、その方針に「構造を持つ既定値」と「稼働中に変える業務値」の置き場所を足したものです。
設定項目一覧のテンプレート
設定値は、一覧にして初めて「どこを見ればよいか」が決まります。
| No | キー | 説明 | 型 | 既定値 | 置き場所 | 環境変数名 | 必須 | 秘密 | 変更時の反映方法 | 値の範囲 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | | | | | | | | | | |
| 列 | 書くこと |
|---|---|
| キー | アプリ内での名前。分類.項目 の形でそろえる |
| 型 | int/bool/string/duration など。単位が要るものは説明に書く(秒、件) |
| 既定値 | 何も指定しないときの値。必須の値は「なし」 |
| 置き場所 | 既定値/設定ファイル/環境変数/DB/シークレット管理 |
| 必須 | 無いときに起動を止めるか |
| 秘密 | ログや画面に出すときに伏せるか |
| 変更時の反映方法 | 再起動/デプロイ/即時/読み直しの間隔 |
記入例(架空の備品予約システム)
| No | キー | 説明 | 型 | 既定値 | 置き場所 | 環境変数名 | 必須 | 秘密 | 変更時の反映方法 | 値の範囲 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | app.port | 待ち受けポート | int | 8000 | 設定ファイル | APP_PORT | 任意 | いいえ | 再起動 | 1024〜65535 |
| 2 | app.log_level | ログレベル | string | WARNING | 環境変数 | APP_LOG_LEVEL | 任意 | いいえ | 再起動 | DEBUG/INFO/WARNING/ERROR |
| 3 | app.page_size | 一覧の1ページの件数 | int | 50 | 設定ファイル | APP_PAGE_SIZE | 任意 | いいえ | デプロイ | 10〜200 |
| 4 | database.host | DB のホスト名 | string | なし | 環境変数 | DATABASE_HOST | 必須 | いいえ | 再起動 | — |
| 5 | database.port | DB のポート | int | 5432 | 設定ファイル | DATABASE_PORT | 任意 | いいえ | 再起動 | 1〜65535 |
| 6 | database.pool_size | 接続プールの数 | int | 10 | 既定値 | DATABASE_POOL_SIZE | 任意 | いいえ | 再起動 | 1〜100 |
| 7 | database.password | DB のパスワード | string | なし | シークレット管理 | DATABASE_PASSWORD | 必須 | はい | 再起動 | — |
| 8 | reservation.max_days_ahead | 何日先まで予約できるか | int | 30 | DB | — | 任意 | いいえ | 即時 | 1〜365 |
| 9 | reservation.max_per_user | 1人が同時に持てる予約数 | int | 3 | DB | — | 任意 | いいえ | 即時 | 1〜20 |
| 10 | feature.new_search | 新しい検索画面を出すか | bool | false | 環境変数 | FEATURE_NEW_SEARCH | 任意 | いいえ | 再起動 | true/false |
表計算ソフトに貼るときは、次のタブ区切りを使います。
No キー 説明 型 既定値 置き場所 環境変数名 必須 秘密 変更時の反映方法 値の範囲
1 app.port 待ち受けポート int 8000 設定ファイル APP_PORT 任意 いいえ 再起動 1024〜65535
2 app.log_level ログレベル string WARNING 環境変数 APP_LOG_LEVEL 任意 いいえ 再起動 DEBUG/INFO/WARNING/ERROR
3 app.page_size 一覧の1ページの件数 int 50 設定ファイル APP_PAGE_SIZE 任意 いいえ デプロイ 10〜200
4 database.host DBのホスト名 string なし 環境変数 DATABASE_HOST 必須 いいえ 再起動 —
5 database.port DBのポート int 5432 設定ファイル DATABASE_PORT 任意 いいえ 再起動 1〜65535
6 database.pool_size 接続プールの数 int 10 既定値 DATABASE_POOL_SIZE 任意 いいえ 再起動 1〜100
7 database.password DBのパスワード string なし シークレット管理 DATABASE_PASSWORD 必須 はい 再起動 —
8 reservation.max_days_ahead 何日先まで予約できるか int 30 DB — 任意 いいえ 即時 1〜365
9 reservation.max_per_user 1人が同時に持てる予約数 int 3 DB — 任意 いいえ 即時 1〜20
10 feature.new_search 新しい検索画面を出すか bool false 環境変数 FEATURE_NEW_SEARCH 任意 いいえ 再起動 true/false
優先順位の考え方:引数>環境変数>設定ファイル>既定値
同じキーが複数の場所にあるときは、実行する人に近いほうを優先 します。
| 順位 | 置き場所 | 誰がいつ決めるか |
|---|---|---|
| 1(最優先) | 起動引数 | 実行する人が、その回だけ |
| 2 | 環境変数 | 基盤の担当者が、環境ごとに |
| 3 | 設定ファイル | 開発者が、リリースごとに |
| 4 | コード内の既定値 | 開発者が、最初に |
この順にしておくと、「普段は設定ファイルの値、本番だけ環境変数で上書き、調査のときだけ引数で変える」という使い方ができます。DB の業務値はこの順位の外に置き、同じキーを DB と環境変数の両方に持たせない ようにすると、どちらが効いているかで迷いません。
Pythonで優先順位を解決するコードと実行結果
標準ライブラリだけで、4つの置き場所から値を決め、どこから採用したかも表示します。
# settings.toml 構造を持つ既定値(リポジトリに入れる。秘密情報は書かない)
[app]
port = 8080
log_level = "INFO"
page_size = 20
[database]
host = "localhost"
port = 5432
# config.py 引数 > 環境変数 > 設定ファイル > 既定値 の順で設定値を決める(Python 3.11、標準ライブラリのみ)
import argparse
import os
import sys
import tomllib
def to_bool(v) -> bool:
if isinstance(v, bool):
return v
if str(v).lower() in ("1", "true", "yes", "on"):
return True
if str(v).lower() in ("0", "false", "no", "off"):
return False
raise ValueError(v)
# キー: (型, 既定値, 必須か, 秘密か)
SPEC = {
"app.port": (int, 8000, False, False),
"app.log_level": (str, "WARNING", False, False),
"app.page_size": (int, 50, False, False),
"database.host": (str, None, True, False),
"database.port": (int, 5432, False, False),
"database.pool_size": (int, 10, False, False),
"database.password": (str, None, True, True),
"feature.new_search": (to_bool, False, False, False),
}
def to_env(key: str) -> str:
# app.port -> APP_PORT / database.host -> DATABASE_HOST
return key.replace(".", "_").upper()
def load_file(path: str) -> dict:
if not os.path.exists(path):
return {}
with open(path, "rb") as f:
data = tomllib.load(f)
return {f"{sec}.{k}": v for sec, table in data.items() for k, v in table.items()}
def resolve(argv=None, environ=None, path="settings.toml"):
environ = os.environ if environ is None else environ
parser = argparse.ArgumentParser()
for key in SPEC:
parser.add_argument("--" + key.replace(".", "-").replace("_", "-"), dest=key)
args = {k: v for k, v in vars(parser.parse_args(argv)).items() if v is not None}
file_values = load_file(path)
values, sources, errors = {}, {}, []
for key, (typ, default, required, _secret) in SPEC.items():
if key in args:
raw, src = args[key], "引数"
elif environ.get(to_env(key), "") != "": # 空文字は「設定なし」として扱う
raw, src = environ[to_env(key)], "環境変数"
elif key in file_values:
raw, src = file_values[key], "設定ファイル"
elif not required:
raw, src = default, "既定値"
else:
if required:
errors.append(f"{key}: 必須の値がありません(環境変数 {to_env(key)} を設定してください)")
continue
try:
values[key] = typ(raw)
sources[key] = src
except ValueError:
errors.append(f"{key}: {raw!r} を {typ.__name__} に変換できません({src})")
if errors:
print("設定エラーのため起動を中止します:", file=sys.stderr)
for e in errors:
print(" - " + e, file=sys.stderr)
sys.exit(1)
return values, sources
if __name__ == "__main__":
values, sources = resolve()
for key, (_t, _d, _r, secret) in SPEC.items():
shown = "********" if secret else values[key]
print(f"{key:<20} = {shown!s:<10} ({sources[key]})")
引数・環境変数・設定ファイル・既定値が混ざった状態で実行した結果です。
$ DATABASE_PASSWORD=s3cret DATABASE_HOST=db.internal APP_LOG_LEVEL=DEBUG python3 config.py --app-port 9000
app.port = 9000 (引数)
app.log_level = DEBUG (環境変数)
app.page_size = 20 (設定ファイル)
database.host = db.internal (環境変数)
database.port = 5432 (設定ファイル)
database.pool_size = 10 (既定値)
database.password = ******** (環境変数)
feature.new_search = False (既定値)
app.port は設定ファイルに 8080、コードの既定値に 8000 がありますが、引数の 9000 が採用されています。パスワードは値を出さず、どこから来たかだけを表示しています。採用元を起動ログに出しておく と、「なぜこの値になっているか」をすぐに追えます。
起動時に必須値の欠落で止める
必須の値が無いまま起動すると、最初のリクエストが来るまで気づけません。起動時にまとめて検証し、足りないものを一度に全部表示して終了 します。上のコードの errors がその役割です。
必須の値を渡さずに起動した結果:
$ python3 config.py; echo "終了コード: $?"
設定エラーのため起動を中止します:
- database.password: 必須の値がありません(環境変数 DATABASE_PASSWORD を設定してください)
終了コード: 1
環境変数が空文字のとき:
$ DATABASE_PASSWORD= DATABASE_HOST=db.internal python3 config.py; echo "終了コード: $?"
設定エラーのため起動を中止します:
- database.password: 必須の値がありません(環境変数 DATABASE_PASSWORD を設定してください)
終了コード: 1
型が合わない値が複数あるとき:
$ DATABASE_PASSWORD=s3cret APP_PORT=abc FEATURE_NEW_SEARCH=maybe python3 config.py; echo "終了コード: $?"
設定エラーのため起動を中止します:
- app.port: 'abc' を int に変換できません(環境変数)
- feature.new_search: 'maybe' を to_bool に変換できません(環境変数)
終了コード: 1
真偽値に専用の変換関数(to_bool)を使っているのは、Python の bool() が文字列の中身を見ないためです。
$ python3 -c 'print(bool("false"), bool("0"), bool(""))'
True True False
FEATURE_NEW_SEARCH=false と書いたつもりが有効になる、という取り違えを防げます。
Javaで優先順位を解決するコードと実行結果
同じ順序を、Java 21 の標準ライブラリだけで書いた例です。
# application.properties 既定値をまとめたファイル
app.port=8080
app.log-level=INFO
app.page-size=20
database.host=localhost
database.port=5432
// ConfigDemo.java 引数 > 環境変数 > 設定ファイル > 既定値(Java 21、標準ライブラリのみ)
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class ConfigDemo {
record Item(String defaultValue, boolean required, boolean secret) {}
static final Map<String, Item> SPEC = new LinkedHashMap<>();
static {
SPEC.put("app.port", new Item("8000", false, false));
SPEC.put("app.log-level", new Item("WARNING", false, false));
SPEC.put("app.page-size", new Item("50", false, false));
SPEC.put("database.host", new Item(null, true, false));
SPEC.put("database.port", new Item("5432", false, false));
SPEC.put("database.pool-size", new Item("10", false, false));
SPEC.put("database.password", new Item(null, true, true));
}
// app.log-level -> APP_LOGLEVEL(ドットを _ に、ハイフンを取り除き、大文字にする)
static String toEnv(String key) {
return key.replace(".", "_").replace("-", "").toUpperCase();
}
public static void main(String[] args) throws IOException {
Map<String, String> argValues = new LinkedHashMap<>();
for (String a : args) { // --app.port=9000 の形を読む
if (a.startsWith("--") && a.contains("=")) {
int i = a.indexOf('=');
argValues.put(a.substring(2, i), a.substring(i + 1));
}
}
Properties file = new Properties();
Path path = Path.of("application.properties");
if (Files.exists(path)) {
try (Reader r = Files.newBufferedReader(path)) {
file.load(r);
}
}
Map<String, String> env = System.getenv();
List<String> errors = new ArrayList<>();
Map<String, String[]> resolved = new LinkedHashMap<>();
SPEC.forEach((key, item) -> {
String envValue = env.get(toEnv(key));
if (argValues.containsKey(key)) {
resolved.put(key, new String[] {argValues.get(key), "引数"});
} else if (envValue != null && !envValue.isEmpty()) {
resolved.put(key, new String[] {envValue, "環境変数"});
} else if (file.containsKey(key)) {
resolved.put(key, new String[] {file.getProperty(key), "設定ファイル"});
} else if (!item.required()) {
resolved.put(key, new String[] {item.defaultValue(), "既定値"});
} else {
errors.add(key + ": 必須の値がありません(環境変数 " + toEnv(key) + " を設定してください)");
}
});
if (!errors.isEmpty()) {
System.err.println("設定エラーのため起動を中止します:");
errors.forEach(e -> System.err.println(" - " + e));
System.exit(1);
}
resolved.forEach((key, v) -> {
String shown = SPEC.get(key).secret() ? "********" : v[0];
System.out.printf("%-20s = %-12s (%s)%n", key, shown, v[1]);
});
}
}
$ DATABASE_PASSWORD=s3cret DATABASE_HOST=db.internal APP_LOGLEVEL=DEBUG java ConfigDemo.java --app.port=9000
app.port = 9000 (引数)
app.log-level = DEBUG (環境変数)
app.page-size = 20 (設定ファイル)
database.host = db.internal (環境変数)
database.port = 5432 (設定ファイル)
database.pool-size = 10 (既定値)
database.password = ******** (環境変数)
$ java ConfigDemo.java; echo "終了コード: $?"
設定エラーのため起動を中止します:
- database.password: 必須の値がありません(環境変数 DATABASE_PASSWORD を設定してください)
終了コード: 1
端末の文字コードの設定によって日本語が ? で表示される場合は、java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 ConfigDemo.java のように出力の文字コードを指定します。
Spring Bootの優先順位(公式リファレンスの順序)
Spring Boot は、この優先順位の解決を標準で備えています。Spring Boot 4.1.1 のリファレンス「Externalized Configuration」には、後に挙げたものが前のものを上書きする 順序として次の一覧があります(主なものを抜粋。番号はリファレンスの番号)。
| 番号 | 置き場所 | この記事の分類 |
|---|---|---|
| 1 | 既定のプロパティ(SpringApplication.setDefaultProperties) |
既定値 |
| 3 | 設定データ(application.properties や YAML) |
設定ファイル |
| 5 | OS の環境変数 | 環境変数 |
| 6 | Java のシステムプロパティ(-D で渡す値) |
起動時の指定 |
| 10 | SPRING_APPLICATION_JSON のプロパティ |
環境変数(JSON でまとめて渡す) |
| 11 | コマンドライン引数 | 引数 |
番号が大きいほど優先されるので、引数 > 環境変数 > 設定ファイル > 既定値 の順と一致します。設定データの中にも順序があり、後のものが優先されます。
- jar の中の
application.properties - jar の中の
application-{profile}.properties - jar の外の
application.properties - jar の外の
application-{profile}.properties
環境変数の名前については、多くの OS がピリオド区切りのキーを使えないため、アンダースコアで代用できる(spring.config.name の代わりに SPRING_CONFIG_NAME)とリファレンスに書かれています。
必須値の検証は、@ConfigurationProperties のクラスに @Validated と制約のアノテーションを付けます。リファレンスに載っている形は次のとおりです。
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotEmpty;
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotEmpty
private String username;
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
}
稼働中に変える業務値をDBに置く
運用担当者が画面から変える値は、設定テーブルに置きます。誰がいつ変えたかを残せる ことが、環境変数や設定ファイルとの違いです。
-- 稼働中に変える業務値を置くテーブル(PostgreSQL 16)
CREATE TABLE app_settings (
setting_key varchar(100) PRIMARY KEY,
setting_value text NOT NULL,
value_type varchar(10) NOT NULL CHECK (value_type IN ('int', 'bool', 'string')),
description text NOT NULL,
updated_by varchar(50) NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO app_settings (setting_key, setting_value, value_type, description, updated_by) VALUES
('reservation.max_days_ahead', '30', 'int', '何日先まで予約できるか', 'admin'),
('reservation.max_per_user', '3', 'int', '1人が同時に持てる予約数', 'admin'),
('notice.banner_enabled', 'false', 'bool', 'お知らせ帯を表示するか', 'admin');
UPDATE app_settings
SET setting_value = '5', updated_by = 'sato', updated_at = now()
WHERE setting_key = 'reservation.max_per_user';
SELECT setting_key, setting_value, value_type, updated_by FROM app_settings ORDER BY setting_key;
setting_key | setting_value | value_type | updated_by
----------------------------+---------------+------------+------------
notice.banner_enabled | false | bool | admin
reservation.max_days_ahead | 30 | int | admin
reservation.max_per_user | 5 | int | sato
(3 rows)
一覧に無い型を入れようとすると、CHECK 制約で止まります。
ERROR: new row for relation "app_settings" violates check constraint "app_settings_value_type_check"
DB に置くときに決めておくことは次の3点です。
| 決めること | 選択肢と目安 |
|---|---|
| 読み直しのタイミング | リクエストごとに読む(常に最新。問い合わせが増える)/一定間隔で読み直す(数十秒〜数分の遅れを許容)/変更時に通知して読み直す |
| DB から読めないとき | 直前に読めた値を使い続ける。初回から読めないときはコードの既定値を使う |
| 値の検証 | 画面で保存するときに型と範囲を確かめる。値の列は文字列になるため、読む側でも型変換でエラーにする |
設定値と定数・マスタデータ・機能フラグの違い
| 種類 | 何を表すか | 変える人 | 変える頻度 | 置き場所 | 例 |
|---|---|---|---|---|---|
| 定数 | 仕様として決まっていて変わらない値 | 開発者(仕様変更時のみ) | ほぼ無い | コード | 1日の秒数、状態コードの定義 |
| 設定値 | 動きを調整する値。環境や運用で変わる | 開発者・基盤の担当者・運用担当者 | リリースごと〜随時 | 既定値/設定ファイル/環境変数/DB | 接続先、タイムアウト、上限件数 |
| マスタデータ | 業務で扱う対象の一覧 | 業務の担当者 | 随時 | DB のマスタテーブル | 部署、備品の種類、会議室 |
| 機能フラグ | 機能を出すか出さないかの切り替え | 開発者・リリースの担当者 | リリースの前後に集中 | 環境変数、またはフラグ管理の仕組み | 新しい検索画面の表示 |
どれにするかの判断基準
| 条件 | 選ぶもの |
|---|---|
| 値を変えるとテストのやり直しが必要になる | 定数(コードの変更として扱う) |
| 行が増減し、ほかのテーブルから参照される | マスタデータ |
| 値が1つで、キーで引く | 設定値 |
| 一時的な切り替えで、役目を終えたら消す | 機能フラグ |
| 環境ごとに値が違う | 設定値(環境変数) |
設定値にしすぎると、組み合わせの数が増えて確かめきれなくなります。変える予定のない値は定数のままにする ほうが、一覧が短く保てます。
確かめて分かったこと
- 環境変数は 空文字でも「設定されている」扱い になる。
DATABASE_PASSWORD=のように値を空で渡した場合に備え、空文字を「設定なし」と同じに扱う1行を入れておくと、必須の検証で止められる。 - Python の
bool("false")はTrue。真偽値は専用の変換関数 を通す。 - エラーを1件目で止めずに集めて表示すると、型の合わない値が2つあるときも1回の起動で両方わかる。
- 採用元(引数・環境変数・設定ファイル・既定値)を表示 すると、設定ファイルの 8080 とコードの 8000 と引数の 9000 のどれが効いたかが一目でわかる。
- Spring Boot の公式の順序は「後のものが優先」と書かれていて、一覧の上にあるほど弱い。「上が強い」と読み違えやすい。
- 設定テーブルの値は文字列の列に入るため、
value_typeに CHECK 制約を付けておくと、想定外の型の行が入る前に DB が止めてくれる。
つまずきやすい点と直し方
| 状況 | 起きること | 直し方 |
|---|---|---|
| コードのあちこちで環境変数を直接読む | どの設定があるのか一覧できない | 読み込みを1か所にまとめ、ほかは設定オブジェクトを受け取る |
| 必須の値にも既定値を入れている | 本番で開発用の接続先のまま動く | 環境で変わる値は既定値を「なし」にして必須にする |
| 秘密情報を設定ファイルに書く | リポジトリの履歴に残る | シークレット管理に置き、環境変数で渡す。ファイルにはキー名だけ書く |
| 秘密情報を起動引数で渡す | プロセス一覧やシェルの履歴に見える | 環境変数か、ファイルの受け渡しにする |
| 起動ログに設定値をそのまま出す | パスワードがログに残る | 一覧の「秘密」列を見て伏せ字にする |
| 同じキーを DB と環境変数の両方に持つ | どちらが効くのか人によって理解が違う | キーごとに置き場所を1つに決め、一覧に書く |
よくある質問
Q. 設定値は環境変数と設定ファイルのどちらに置けばよいですか? A. 環境ごとに値が変わるなら環境変数、どの環境でも同じ既定値で構造を持つなら設定ファイルです。両方に書いた場合は環境変数を優先させます。
Q. 環境変数と設定ファイルの優先順位はどうなりますか? A. 引数 > 環境変数 > 設定ファイル > 既定値の順が一般的です。Spring Boot の公式の順序もこの並びになっています。
Q. 設定値をDBに置くのはどんなときですか? A. 再起動なしで運用担当者が変える業務値のときです。変更者と日時を残せます。DB の接続先やパスワードのように、DB を読む前に必要な値は置けません。
Q. 設定値と定数の違いは何ですか? A. 定数は仕様として固定された値で、変更はコードの修正として扱います。設定値はコードを変えずに調整する値です。迷ったら「値を変えたあとにテストをやり直すか」で分けます。
まとめ
- 置き場所は 値の性質 で決める。環境で変わる値は環境変数、構造を持つ既定値は設定ファイル、稼働中に変える業務値は DB。
- 秘密情報はシークレット管理 に置き、環境変数として渡す。設定ファイル・DB・引数には書かない。
- 優先順位は 引数 > 環境変数 > 設定ファイル > 既定値。採用元を起動ログに出す。
- 必須の値が無ければ起動時に止め、足りないものを一度に全部表示する。
- 設定項目は キー・型・既定値・置き場所・反映方法・必須 を一覧にする。
- 空文字の環境変数と文字列の真偽値 は検証でつまずきやすい。専用の扱いを入れる。
参考・出典
- Spring Boot 4.1.1 リファレンス「Externalized Configuration」(docs.spring.io、2026-10-06 確認)
- The Twelve-Factor App「III. Config」(12factor.net)
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「設定値の管理設計|環境変数・設定ファイル・DBの使い分けと優先順位」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。