こんにちは。ディベロップメントサービス1課の山本です。
今回はコスト削減のため AWS Lambda (以降、Lambda) を使って Amazon Neptune(以降、Neptune) を自動起動・停止してみました。
この記事の対象者は?
- Neptune を利用されている方
- Neptune のコストを抑えたい方
Neptune のコストについて
コンソール上で Neptune を作成する際に表示される、本番稼働用テンプレートのデフォルト値で計算してみます。条件は以下の通りです。
| 項目 | 値 |
|---|---|
| タイプ | Provisioned |
| クラスターストレージ設定 | Neptune I/O 最適化 |
| インスタンスクラス | db.r6g.xlarge |
| マルチ AZ 配置 | 有(アクティブ/スタンバイ) |
db.r6g.xlarge の時間単価は以下の通りです。
| インスタンスあたりの料金 (USD/時) |
|---|
| 1.071 |
1ヶ月の Neptune のコストはこちらです。(2025年4月8日 時点 東京リージョン)
- 1.071 (USD/時) * 24 (時) * 30(日) * 2 (アクティブ/スタンバイ) * 148 (円/USD) = 約 228,000 円
- 注)今回の取り組みで節約できるインスタンス料金のみ抜粋
- 注)為替レートにより変動します
1ヶ月起動しているだけで約 23 万円とめちゃめちゃ高い。
起動時間を半分に減らすことができれば、月 10 万円以上の節約になります。
構成図
作成する AWS リソースの構成図は以下の通りです。

定時起動する Amazon EventBridge (以降、EventBridge)で Lambda 関数を呼び出して、関数内で Neptune の起動・停止処理を行います。
ファイル構成
AWS Serverless Application Model(以降、SAM)を利用して Lambda 関数をデプロイします。
. ├── samconfig.toml ├── src │ └── app.py └── template.yaml
samconfig.toml
SAM の設定ファイルとなります。
起動は平日の朝8時。停止は毎日20時(夜)に設定するようにします。
いずれも JST(日本時間)の設定です。
version = 0.1
[default.deploy.parameters]
stack_name = "blog-auto-neptune-start-stop"
capabilities = "CAPABILITY_NAMED_IAM"
region = "ap-northeast-1"
s3_bucket = "{リソースを格納するS3バケット名}"
parameter_overrides = [
"SystemName=blog", # システム名
"NeptuneClusterId=blog-neptune", # Neptune クラスターID(識別子)
'StartCronExpression="cron(0 8 ? * MON-FRI *)"', # Neptune 起動時間(JST)
'StopCronExpression="cron(0 20 ? * * *)"' # Neptune 終了時間(JST)
]
template.yaml
SAM のテンプレートファイルとなります。
以下のリソースを作成します。
- Lambda × 1
- EventBridge スケジュール × 2
- IAM Role × 1
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: SAM Template for Neptune Auto Start/Stop Parameters: SystemName: Type: String Description: The name of the system NeptuneClusterId: Type: String Description: The ID of the Neptune cluster to start/stop StartCronExpression: Type: String Default: "cron(0 8 ? * MON-FRI *)" Description: "Cron expression for starting the Neptune cluster" StopCronExpression: Type: String Default: "cron(0 20 ? * * *)" Description: "Cron expression for stopping the Neptune cluster" Resources: NeptuneStartStopFunction: Type: AWS::Serverless::Function Properties: FunctionName: !Sub ${SystemName}-neptune-start-stop Description: "Neptune の自動起動・停止 Lambda" Handler: app.handler Runtime: python3.13 CodeUri: ./src Environment: Variables: NEPTUNE_CLUSTER_ID: !Ref NeptuneClusterId Policies: - Version: "2012-10-17" Statement: - Effect: Allow Action: - rds:StartDBCluster - rds:StopDBCluster Resource: !Sub arn:aws:rds:${AWS::Region}:${AWS::AccountId}:cluster:${NeptuneClusterId} StartScheduler: Type: AWS::Scheduler::Schedule Properties: Name: !Sub ${SystemName}-start-neptune Description: Neptune 起動スケジュール ScheduleExpression: !Ref StartCronExpression ScheduleExpressionTimezone: "Asia/Tokyo" FlexibleTimeWindow: Mode: "OFF" Target: Arn: !GetAtt NeptuneStartStopFunction.Arn RoleArn: !GetAtt LambdaInvokeRole.Arn Input: '{"action": "start"}' StopScheduler: Type: AWS::Scheduler::Schedule Properties: Name: !Sub ${SystemName}-stop-neptune Description: Neptune 停止スケジュール ScheduleExpression: !Ref StopCronExpression ScheduleExpressionTimezone: "Asia/Tokyo" FlexibleTimeWindow: Mode: "OFF" Target: Arn: !GetAtt NeptuneStartStopFunction.Arn RoleArn: !GetAtt LambdaInvokeRole.Arn Input: '{"action": "stop"}' LambdaInvokeRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ${SystemName}-scheduler-lambda-invoke-role AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: "scheduler.amazonaws.com" Action: "sts:AssumeRole" Policies: - PolicyName: !Sub ${SystemName}-scheduler-lambda-invoke-policy PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "lambda:InvokeFunction" Resource: - !GetAtt NeptuneStartStopFunction.Arn
app.py
Lambda 関数で実行されるプログラムファイルとなります。
boto3 を用いて、開始と停止を行うだけです。
import os import boto3 neptune_client = boto3.client('neptune') def handler(event, _context): cluster_id = os.environ['NEPTUNE_CLUSTER_ID'] action = event.get('action') if action == 'start': neptune_client.start_db_cluster(DBClusterIdentifier=cluster_id) elif action == 'stop': neptune_client.stop_db_cluster(DBClusterIdentifier=cluster_id) else: raise ValueError("Invalid action. Use 'start' or 'stop'.")
実行結果
自動起動
無事、自動起動できてます。

自動停止
無事、自動停止できてます。

まとめ
- Neptune は性能・可用性を求めるとコストがかなり高くなる(約23万円/月)
- Lambda を使って、自動起動・停止してコストを削減しよう
- Neptune の起動には時間がかかる(体感 20分程度)ため、早めのスケジュール設定が必要
さいごに
自動停止後に残業で再起動することのないように。
本ブログがどなたかのお役に立てれば幸いです。