A personalized toolkit for provisioning and managing Docker services on a server environment (*.cansu.dev/*
), primarily built for the my specific needs.
cansu.dev
). While parts might be adaptable, it relies on specific conventions (like 1Password secret structures) and may require significant modification for general use. It serves partly as infrastructure-as-code and partly as personal documentation.
- Docker-centric: Manages Docker networks, volumes, containers, and images.
- 1Password Integration: Securely fetches credentials (database passwords, API keys, etc.) using a 1Password Service Account. This is a hard dependency for commands requiring secrets.
- Cobra CLI: Provides a structured command-line interface.
- Configuration Driven: Uses a TOML file (
~/.oblivion.toml
) for defining container names, ports, image tags, network names, etc. - Service Provisioning: Includes commands to set up:
- Docker Networks
- PostgreSQL (Primary + Replica + PgBouncer)
- Static File Server (Nginx with specific permissions)
- Uptime Kuma (Monitoring)
- Observer Stack (Grafana, Prometheus, Loki, cAdvisor, Node Exporter, Alertmanager)
- Redis (DragonflyDB)
- A custom "Playground" backend service.
- Backup Helpers: Includes shell scripts using
wal-g
for PostgreSQL backups (requires specific setup).
- Go: Version 1.24 or higher (see
go.mod
). - Docker: Docker Engine and Docker CLI installed and running. The user running
oblivion
needs permission to interact with the Docker socket. - 1Password CLI: Installed and configured.
- 1Password Service Account: A 1Password Service Account token must be available via the
OP_SERVICE_ACCOUNT_TOKEN
environment variable for commands requiring secrets. sudo
: Required for some operations likestatic permissions
(usessetfacl
) and thebackup/link.sh
script.jq
: Required by thebackup/link.sh
script to parse Docker volume paths.acl
package: Required on Linux systems forsetfacl
used bystatic permissions
. (e.g.,sudo apt install acl
on Debian/Ubuntu).- (Optional)
ufw
: Used in the example firewall setup documented below.
- Clone the repository:
git clone https://github.com/caner-cetin/oblivion.git cd oblivion
- Build the binary:
go build -o oblivion .
- (Optional) Move the binary to a location in your
$PATH
:sudo mv oblivion /usr/local/bin/
- On the first run, if
~/.oblivion.toml
does not exist, Oblivion will create one with default values. - Location: Defaults to
$HOME/.oblivion.toml
. You can specify a different path using the--config
flag. - Review and Customize: It is crucial to review and customize this file. Pay special attention to:
[Docker].Socket
: Ensure this points to your Docker socket (default isunix:///var/run/docker.sock
).[Networks]
: Verify or change network names if desired.[Onepass].vault_name
: Set this to the name of the 1Password Vault where your secrets are stored.[Static].uploader_user
: Set to the username that will upload files to the static server path.[Static].static_path
: The absolute path on the host for static files.[Observer].Binds
: Critical: These default to the my local paths. You must update these bind mount source paths to point to your actual configuration directories for Prometheus, Grafana, Alertmanager, and Loki.- Other service-specific configurations (ports, container names, image tags, volumes).
- Service Account Token: Export the token before running commands that need secrets:
export OP_SERVICE_ACCOUNT_TOKEN="ops_your_token_here..."
- Vault: Ensure the vault specified in
[Onepass].vault_name
exists. - Secret Structure: Oblivion expects secrets to be stored in 1Password using a specific convention. Secret references in the code and documentation follow this pattern:
op://<Vault Name>/<Item Title>/<Section Name>/<Field Name>
orop://<Vault Name>/<Item Title>/<Field Name>
if no section is used.- Example: A required secret
"/Postgres/Replicator/username"
means:- In the vault configured in
.oblivion.toml
(e.g., "Server")... - Find or create a Login or Secure Note item titled "Postgres".
- Within that item, find or create a section named "Replicator".
- Within that section, find or create a field named "username" and store the value there.
- In the vault configured in
- Example: A required secret
The general command structure is:
oblivion [command] [subcommand] [flags]
Manages required Docker networks.
oblivion networks up
- Creates Docker bridge networks defined in the
[Networks]
section of the config (e.g.,database_bridge
,uptime_bridge
,grafana_bridge
,loki_bridge
). - This should typically be run first.
- Creates Docker bridge networks defined in the
Manages PostgreSQL primary, replica, and PgBouncer containers.
- Required Secrets:
/Postgres/Replicator/username
/Postgres/Replicator/password
/Postgres/Root/username
(Superuser)/Postgres/Root/password
(Superuser)/Postgres/Bouncer/username
/Postgres/Bouncer/password
oblivion postgres up
- Pulls necessary images (
postgres:17
,edoburu/pgbouncer
by default). - Creates Docker volumes (
pg_primary_data
,pg_replica_data
by default). - Starts the primary PostgreSQL container, waits for it to be healthy.
- Starts the replica PostgreSQL container.
- Starts the PgBouncer container, configured to connect to the primary.
- Pulls necessary images (
- PgBouncer SQL Setup: For PgBouncer authentication using
AUTH_QUERY
, you need to execute the following SQL on your primary PostgreSQL instance for each database you intend to connect to via PgBouncer. Replace'i_look_cute_in_maid_outfit'
with the actual password you stored in 1Password for/Postgres/Bouncer/password
.-- Run this logged in as the Postgres superuser CREATE ROLE pgbouncer LOGIN PASSWORD 'i_look_cute_in_maid_outfit'; -- Use the actual password here! -- Create the lookup function in the 'public' schema (or desired schema) CREATE OR REPLACE FUNCTION public.lookup ( INOUT p_user name, OUT p_password text ) RETURNS record LANGUAGE sql SECURITY DEFINER SET search_path = pg_catalog AS $$SELECT usename, passwd FROM pg_shadow WHERE usename = p_user$$; -- Restrict execution permissions to the 'pgbouncer' role REVOKE EXECUTE ON FUNCTION public.lookup(name) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.lookup(name) TO pgbouncer;
Manages a static file server using Nginx.
oblivion static up
- Builds a custom Nginx image (
cansu.dev-static-nginx
by default) if it doesn't exist, including configuration for auto-indexing. - Starts the Nginx container, binding the configured host port to container port 80.
- Mounts the
[Static].static_path
from the host into the container.
- Builds a custom Nginx image (
oblivion static permissions
- Requires
sudo
and theacl
package. - Sets complex ownership and permissions on the
[Static].static_path
. - Purpose: Allows the specified
uploader_user
(and their group) to write files, while ensuring the Nginx process (running as a different user inside the container, typicallynginx
orwww-data
) can read them. Useschown
,chmod
, andsetfacl
for fine-grained control and default ACLs for new files/directories. Review thecmd/static.go:staticChmod
function for exact commands.
- Requires
fancyindex
Note: If the themed directory index (fancyindex
) doesn't load correctly afteroblivion static up
(e.g., 404s for header/footer), you might need to manually ensure the contents ofcmd/config/static/fancyindex/
are present at<your_static_path>/fancyindex/
. Ideally, these files should be copied into the custom Nginx image during the build.
Manages an Uptime Kuma instance.
oblivion kuma up
- Pulls the Uptime Kuma image (
louislam/uptime-kuma:1
by default). - Creates a data volume (
kuma_kuma_data
by default). - Starts the container, exposing the configured port (default
3001
). - Connects the container to the
database_network_name
anduptime_network_name
(by default), allowing it to monitor services on those networks using their container names (e.g.,cansu.dev-pg-primary:5432
).
- Pulls the Uptime Kuma image (
Manages a monitoring and observability stack.
- Required Secrets:
/Grafana/Admin/Username
/Grafana/Admin/Password
oblivion observer up
- Important: Verify and update the host paths in
[Observer].Binds
in your.oblivion.toml
before running! - Pulls images for Grafana, Prometheus, Loki, cAdvisor, Node Exporter, and Alertmanager.
- Creates volumes for Grafana and Prometheus data.
- Starts all component containers with appropriate configurations, port bindings, and network attachments (
grafana_bridge
,loki_bridge
). - Configures Grafana admin credentials using secrets from 1Password.
- Important: Verify and update the host paths in
Manages a Redis-compatible cache/database (using DragonflyDB).
- Required Secrets:
/Redis/password
oblivion redis up
- Pulls the DragonflyDB image (
docker.dragonflydb.io/dragonflydb/dragonfly
by default). - Creates a data volume (
dragonflydata
by default). - Starts the container, exposing the configured port (default
6379
) and requiring the password fetched from 1Password. - Connects to the
database_network_name
.
- Pulls the DragonflyDB image (
Manages a custom backend service defined by the Cansu.
- Required Secrets:
/Postgres/Playground/username
/Postgres/Playground/password
/Redis/password
(assumes same Redis instance asredis up
)/Hugging Face/API Key
oblivion playground up
- Clones the repository specified in
[Playground.Backend].repository
into a temporary directory. - Builds a Docker image (
playground-backend
by default) from thebackend
subdirectory of the cloned repo. - Starts the container, injecting database URLs, Redis URLs, Hugging Face tokens, etc., as environment variables using secrets from 1Password.
- Connects to
database_network_name
andloki_network_name
. - Note: Starts the container with elevated privileges (
seccomp:unconfined
,SYS_ADMIN
, host PID/Cgroup namespaces, Docker socket mount). This is likely required for the backend's specific function (e.g., running code, interacting with Docker) and implies security considerations.
- Clones the repository specified in
These are helper scripts for backing up PostgreSQL using wal-g
to an S3-compatible backend (like Cloudflare R2). They require manual setup and execution.
backup/.env
: This file defines environment variables needed bywal-g
andop run
. You must edit this file and ensure theop://
references point to the correct secrets in your 1Password vault.backup/link.sh
:- Purpose:
wal-g
often needs direct access to the PostgreSQL data directory. Docker volume paths can be complex. This script finds the host path of the primary PostgreSQL container's data volume and creates a symbolic link at/var/lib/postgresql/data
(a common default path) pointing to it. - Requires
sudo
andjq
. - Run this script once (or whenever the volume path might change, though it's usually stable):
sudo ./backup/link.sh
- Purpose:
backup/backup.sh
:- Purpose: Performs a
wal-g backup-push
. It usesop run --env-file=.env
to inject secrets (like AWS keys, libsodium key, PG password) from 1Password into thesudo -E wal-g ...
command environment. (-E
preserves the environment forsudo
). - Requires
sudo
. - Set your
OP_SERVICE_ACCOUNT_TOKEN
environment variable first. - Run the script:
./backup/backup.sh
(Consider scheduling this withcron
).
- Purpose: Performs a
This section contains notes relevant to the my specific server environment (Debian/Ubuntu). Adapt as needed for your OS/firewall.
# Install ufw if needed
sudo apt update && sudo apt install ufw
# Ensure IPv6 is enabled (usually is by default)
# Check/edit /etc/default/ufw if necessary: IPV6=yes
# Set defaults (Deny incoming, Allow outgoing)
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow essential access
sudo ufw allow ssh # Or your custom SSH port
# Allow access for web traffic reverse proxy (e.g., Cloudflare Tunnel, Caddy, Nginx)
sudo ufw allow http # Port 80
sudo ufw allow https # Port 443
# Allow specific ports if needed (e.g., Cloudflare Tunnel alternative port)
# sudo ufw allow 7844
# Allow access *from* your specific trusted IP address (Replace with your IP)
# sudo ufw allow from 203.0.113.101
# Enable the firewall
sudo ufw enable
# Check status
sudo ufw status verbose
- Linting: Uses
golangci-lint
. Rungolangci-lint run
(configuration is in.golangci.yml
).
This project is primarily for personal use. If you find parts useful, feel free to fork and adapt it. Contributions are unlikely to be merged unless they align with the my specific needs or improve the core tooling in a generally applicable way.
GNU General Public License v3.0
Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.