This repository packages a small OCI Always Free runner that keeps retrying
VM.Standard.A1.Flex launches until capacity becomes available or a matching
instance already exists.
It is extracted from an operational Oracle VM workspace and reduced to the minimum set of files needed to understand, test, deploy, and reproduce the runner on another machine.
launch-a1.shMain retry loop and launch logic.check-runner.shSmall helper to inspect service state, success artifacts, and recent logs.oci-a1-runner.serviceSamplesystemdunit.a1.env.exampleSanitized configuration template.install.shMinimal Linux installer forsystemddeployments with root checks and service-user derivation.verify-install.shLocal post-install verification helper for deployment layout and service wiring.tests/launch-a1.test.shShell regression test with mocked OCI and Discord behavior.tests/check-runner.test.shShell regression test for helper-script path overrides and log display.tests/install.test.shShell regression test for installer path rendering and config preservation.tests/verify-install.test.shShell regression test for local install verification behavior and warning handling.docs/superpowers/specs/2026-04-09-oci-runner-repo-design.mdDesign document for this extracted repository.
launch-a1.sh continuously tries to launch an OCI Always Free
VM.Standard.A1.Flex instance.
Current behavior:
- loads runtime settings from
ENV_FILE - creates and writes logs under
LOG_DIR - queries availability domains dynamically
- checks for an already existing matching instance before each launch cycle
- retries across returned ADs in order
- classifies failures into capacity, transient network, and non-capacity cases
- writes success artifacts after detecting an existing instance or creating a new one successfully
- sends Discord notifications for success and non-capacity failures only
You need a Linux machine with:
bashjqcurlsystemdif you want to run it as a service- OCI CLI installed and authenticated for the target profile
Default install:
sudo bash ./install.sh
sudo editor /home/ubuntu/oci-runner/etc/a1.env
sudo systemctl start oci-a1-runner.serviceQuick validation after start:
bash ./check-runner.sh
sudo systemctl status oci-a1-runner.service --no-pagerYou also need OCI-side prerequisites:
- permission to launch instances in the target compartment
- a valid subnet in the target region
- a valid image in the target region
- a usable SSH authorized keys file
Important environment note:
- The target machine does not have to be the exact original Oracle VM.
- It does need to operate against the same kind of OCI environment described by
your own
a1.env, meaning the machine must be able to use the OCI CLI profile and resource IDs you configure. - If you want to reuse the same subnet, image, and compartment values from an existing deployment, the machine must have access to that same tenancy and resource topology.
- Copy
a1.env.exampletoa1.env. - Replace all placeholder values.
- Confirm
SSH_AUTHORIZED_KEYS_FILEpoints to a real public key file. - Confirm
OCI_CLIpoints to an executable OCI CLI binary. - Create the log directory referenced by
LOG_DIRif you plan to override it.
Minimal example:
cp a1.env.example a1.envKey settings in a1.env:
COMPARTMENT_IDSUBNET_IDIMAGE_IDDISPLAY_NAMESHAPEOCPUSMEMORY_IN_GBSBOOT_VOLUME_SIZE_GBSBOOT_VOLUME_VPUS_PER_GBSUCCESS_SENTINELDISCORD_API_BASEDISCORD_BOT_TOKENDISCORD_CHANNEL_ID
Discord notification settings are optional, but recommended when the runner is left retrying unattended for long periods.
Format note:
a1.envis expected to be a simpleKEY=valuefile.- The launcher only accepts a fixed allowlist of known keys.
- Do not add shell commands, command substitutions, or arbitrary script content.
The runner can send Discord messages for important long-running outcomes. This is optional, but recommended when the retry loop runs unattended for long periods.
Required settings for message delivery:
DISCORD_BOT_TOKENDISCORD_CHANNEL_ID
Optional setting:
DISCORD_API_BASEDefaults tohttps://discord.com/api/v10.
If either DISCORD_BOT_TOKEN or DISCORD_CHANNEL_ID is missing, the runner
skips Discord delivery.
The current runner sends Discord messages for:
- successful launch of a new instance
- detection of an already existing matching instance
- unknown non-capacity failures
The current runner does not send Discord messages for:
- capacity or rate-limit failures
- transient network failures
A TooManyRequests response waits 600 seconds before the next launch. Other
retryable failures keep the configured retry interval.
If the Discord API request fails, the runner logs discord notification failed
and continues running. Notification delivery failure does not stop the runner
or the retry loop.
Example settings:
DISCORD_API_BASE='https://discord.com/api/v10'
DISCORD_BOT_TOKEN='<replace-me>'
DISCORD_CHANNEL_ID='<replace-me>'The sample files assume this deployment shape on the target host:
/home/ubuntu/oci-runner/
bin/launch-a1.sh
bin/check-runner.sh
etc/a1.env
log/
The bundled oci-a1-runner.service points to:
ENV_FILE=/home/ubuntu/oci-runner/etc/a1.envLOG_DIR=/home/ubuntu/oci-runner/logExecStart=/home/ubuntu/oci-runner/bin/launch-a1.sh
If your target layout differs, edit the service file accordingly.
Run the script directly:
ENV_FILE=/path/to/a1.env LOG_DIR=/path/to/log bash ./launch-a1.shThis is useful for first-run validation before enabling systemd.
- Copy
launch-a1.shandcheck-runner.shinto your targetbin/directory. - Copy your real
a1.envinto the targetetc/directory. - Copy
oci-a1-runner.serviceinto/etc/systemd/system/. - Adjust absolute paths if your deployment layout is different.
- Reload and enable the service.
Example:
sudo systemctl daemon-reload
sudo systemctl enable --now oci-a1-runner.serviceinstall.sh is a small Linux-only installer for hosts that already have the
required runtime dependencies such as OCI CLI, jq, curl, and systemd.
It must be run as root.
Supported flags:
--root <path>--start
--root must be an absolute path.
Default install:
sudo bash ./install.shCustom root:
sudo bash ./install.sh --root /srv/oci-runnerInstall and start immediately:
sudo bash ./install.sh --startWhat the installer does:
- creates
<root>/bin,<root>/etc, and<root>/log - installs
launch-a1.shandcheck-runner.shinto<root>/bin - creates
<root>/etc/a1.envfroma1.env.exampleonly when the file is missing - derives the service user from
<root>when it matches/home/<user>/...; otherwise it usesSUDO_USERonly if that account exists - fails fast if it cannot derive a valid existing service user
- renders
/etc/systemd/system/oci-a1-runner.servicesoUser,ENV_FILE,LOG_DIR, andExecStartmatch the selected root - sets ownership and permissions so the service user can read a newly created
a1.envand write logs under<root>/log - runs
systemctl daemon-reloadandsystemctl enable oci-a1-runner.service - runs
systemctl start oci-a1-runner.serviceonly when--startis supplied
Safety notes:
install.shdoes not populate live OCI or Discord secrets.- If
<root>/etc/a1.envalready exists, the installer keeps it unchanged. - If
<root>/etc/a1.envalready exists, it must already be readable by the derived service user or the installer will fail fast. - The installer fails fast unless it is run as
root. - The installer fails fast if
--rootis not absolute. - Review and edit
a1.envbefore the first real service start. - The installer overwrites
/etc/systemd/system/oci-a1-runner.servicewith the rendered unit for the selected root. - Unsupported arguments fail fast.
Use verify-install.sh after installation to check local deployment state.
Default root and service:
bash ./verify-install.shCustom root and service:
bash ./verify-install.sh --root /srv/oci-runner --service custom-oci-a1-runner.serviceCurrent verifier scope:
- checks local file layout under the selected root
- checks required non-empty env keys in
a1.env - checks the effective service configuration, or falls back to the installed unit file content, to confirm the selected root paths
- checks basic
systemctl is-enabledandsystemctl is-activevisibility - runs the installed
check-runner.shhelper in a local-only way
Important limits:
verify-install.shchecks local deployment state only- it does not call OCI APIs or submit a launch request
- inactive or disabled services are reported as
[WARN], not automatic hard failures - blocking local problems are reported as
[FAIL]and cause a non-zero exit
You can inspect the runner with:
bash ./check-runner.shThe helper shows:
systemdservice status- success summary from
a1-success.txtora1-success.json - last 20 lines from
launch-a1.log - recent journal entries
Override support:
SERVICE_NAMEENV_FILELOG_DIRSUCCESS_JSONSUCCESS_TXTRUN_LOG
If SUCCESS_JSON is not provided, the helper will try to read
SUCCESS_SENTINEL from ENV_FILE before falling back to
$LOG_DIR/a1-success.json.
When the runner succeeds, it writes:
a1-success.jsona1-success.txt
These include values such as:
- instance id
- boot volume id
- public IP
- availability domain
- success source
The source is either:
launch-successexisting-instance-check
Run the bundled shell regression test from the repository root:
bash ./tests/install.test.sh ./install.sh
bash ./tests/launch-a1.test.sh ./launch-a1.sh
bash ./tests/check-runner.test.sh ./check-runner.sh
bash ./tests/verify-install.test.sh ./verify-install.shExpected result:
PASS
The test uses mocked OCI CLI and mocked Discord posting. It does not contact live OCI resources.
- The runner exits immediately if
SUCCESS_SENTINELalready exists. - The existing-instance guard matches on
DISPLAY_NAMEandSHAPE. - There is still a small race window between the last existing-instance check and the next launch request.
- Capacity and rate-limit errors are treated as expected retry conditions.
- Transient network errors are logged and snapshotted without Discord alerts.
- Non-capacity errors are snapshotted and sent to Discord.
- The script uses
--no-retryand depends on its own loop plussystemd Restart=on-failurebehavior. - If the script exits cleanly after success, the service does not restart.
- This repository intentionally does not include a real
a1.env. - Do not commit live OCI identifiers, Discord tokens, or private local paths.
- The launcher parses
a1.envas restricted key/value data instead of sourcing it as shell code. - Keep the real
a1.envpermissions restricted on the target machine. - Review
SUCCESS_SENTINELand logs before sharing them, because they may contain instance IDs and public IPs.
This standalone repository was extracted from files that originally existed as:
tmp-launch-a1.shtmp-check-runner.shtmp-oci-a1-runner.servicetmp-launch-a1.test.shtmp-a1.env
The first four were copied with stable names. The env file was converted into a sanitized example.