Creating a GitHub Actions Workflow to Start/Stop a CloudSQL Instance Manually and Automatically Stop at Night
Introduction
In environments such as staging or testing, where services only need to be active when necessary, keeping a CloudSQL instance running constantly results in unnecessary costs. Since we don’t have unlimited funds, reducing costs wherever possible is desirable. However, manually going into the console to start the instance each time it’s needed, and stopping it after use, is a hassle.
To solve this, I created a workflow that allows starting and stopping the instance manually from GitHub, and automatically stops it every night to prevent forgetting.
Workflow Overview
Here’s the workflow I created:
name: Manage Staging Cloud SQL
on:
workflow_dispatch:
inputs:
action:
description: "Action"
required: true
default: "start"
type: choice
options:
- start
- stop
schedule:
- cron: "0 12 * * *" # JST 21:00 (UTC+9)
jobs:
manage-cloudsql:
permissions:
contents: "read"
id-token: "write"
runs-on: ubuntu-latest
steps:
- name: Google Auth
uses: google-github-actions/auth@v2
with:
token_format: access_token
workload_identity_provider: "${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}"
service_account: "${{ secrets.SERVICE_ACCOUNT }}"
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2
- name: Manage CloudSQL Instance
run: |
INSTANCE_NAME=${{ secrets.CLOUDSQL_INSTANCE_NAME_STAGING }}
STATUS=$(gcloud sql instances describe "$INSTANCE_NAME" --format="value(state)")
echo "Requested action: $ACTION"
echo "Current status of the instance: $STATUS"
if [ "$ACTION" = "start" ] && [ "$STATUS" = "RUNNABLE" ]; then
echo "Instance is already running."
exit 0
elif [ "$ACTION" = "stop" ] && [ "$STATUS" != "RUNNABLE" ]; then
echo "Instance is already stopped."
exit 0
fi
if [ "$ACTION" = "start" ]; then
gcloud sql instances patch "$INSTANCE_NAME" \
--activation-policy=ALWAYS \
--async
elif [ "$ACTION" = "stop" ]; then
gcloud sql instances patch "$INSTANCE_NAME" \
--activation-policy=NEVER \
--async
else
echo "Invalid action: $ACTION"
exit 1
fi
env:
ACTION: ${{ github.event.inputs.action || 'stop' }}
Workflow Details
Trigger
The workflow is triggered manually using workflow_dispatch
, allowing the user to choose between starting and stopping the instance using a choice
input.
Additionally, it is scheduled to run automatically every night at 9 PM JST via cron
. In scheduled runs, inputs.action
from workflow_dispatch
isn’t available, so the workflow defaults to stopping the instance.
Authentication and gcloud Setup
To start and stop the CloudSQL instance, the workflow uses the gcloud
command. For authentication, it uses google-github-actions/auth
, and for setting up the gcloud CLI, it uses google-github-actions/setup-gcloud
. The service account used for execution must have the roles/cloudsql.admin
role.
I won’t go into detail about Workload Identity Federation here, but Google’s official documentation is well-written and worth checking out:
Starting and Stopping CloudSQL Instances
Before sending the command, the current status of the instance is checked. If the instance is already in the desired state, the operation is skipped. This is because sending a stop command to an already stopped Cloud SQL instance results in an HTTP 400 error, which causes the GitHub Actions job to be marked as failed. While the instance itself remains unaffected, having failed jobs in the logs can be mentally unpleasant. To avoid that, the workflow handles this case gracefully.
The instance is started or stopped using the gcloud sql instances patch
command. Use the --activation-policy=ALWAYS
option to start, and --activation-policy=NEVER
to stop.
Since starting or stopping an instance can take several minutes, this might lead to the error ERROR: (gcloud.sql.instances.patch) Operation is taking longer than expected.
While the operation usually completes successfully despite the error, it unnecessarily increases GitHub Actions execution time. To avoid this, I use the --async
option to end the job without waiting for completion.
For more details on the patch
command options, refer to:
Finally, by setting ACTION
to ${{ github.event.inputs.action || 'stop' }}
, the workflow supports using the selected action during manual runs and defaults to stop
during scheduled runs.
Conclusion
That’s an overview of a GitHub Actions workflow to start and stop a CloudSQL instance. Most examples online use Cloud Functions and Cloud Scheduler, but I wanted to leverage GitHub Actions, so I went with this approach.
I hope this helps someone else trying to reduce their cloud costs!