diff --git a/Dockerfile b/Dockerfile
index 8fca5e58..0d59e208 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@
FROM docker:24.0.6-cli
# Install dependencies
-RUN apk add bash rsync tzdata dos2unix
+RUN apk add bash rsync tzdata dos2unix jq
# Copy all necessary files into the container (from /pkg in the repository to /app in the container)
COPY pkg app
@@ -37,5 +37,8 @@ ENV USE_DEFAULT_RSYNC_ARGS="true"
# Apply custom rsync args (in addition to the default args)
ENV RSYNC_CUSTOM_ARGS=""
+# Require the Docker Label `nautical-backup.enable=true` to be present on each contianer or it will be skipped.
+ENV REQUIRE_LABEL="false"
+
# Run the entry script and pass all variables to it
ENTRYPOINT [ "bash", "-c", "exec ./entry.sh \"${@}\"", "--"]
diff --git a/README.md b/README.md
index 2538cfaf..ce3cb748 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,8 @@ A simple Docker volume backup tool.
[![Pulls from DockerHub](https://img.shields.io/docker/pulls/minituff/nautical-backup?logo=docker)](https://hub.docker.com/r/minituff/nautical-backup)
- [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/minituff/nautical-backup)](https://hub.docker.com/r/minituff/nautical-backup)
- [![Docker Image Size (tag)](https://img.shields.io/docker/image-size/minituff/nautical-backup/latest)](https://hub.docker.com/r/minituff/nautical-backup)
+ [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/minituff/nautical-backup?label=latest%20version)](https://hub.docker.com/r/minituff/nautical-backup)
+ [![Docker Image Size (tag)](https://img.shields.io/docker/image-size/minituff/nautical-backup/latest?label=size)](https://hub.docker.com/r/minituff/nautical-backup)
@@ -23,6 +23,7 @@ Full documentation is available at [https://minituff.github.io/nautical-backup](
Docker Compose
```yaml
+version: '3'
services:
nautical-backup:
image: minituff/nautical-backup:0.1.1
diff --git a/docs/arguments.md b/docs/arguments.md
index 63d4a1e5..16c4caa1 100644
--- a/docs/arguments.md
+++ b/docs/arguments.md
@@ -2,6 +2,10 @@ Nautical provides configuration in the form of Docker enviornment variables.
See the [Installation Section](./installation.md), which contains a few examples of applying enviornment variables.
+### Enviornment Variable vs Label Priority
+If a container has an Enviornment Variable applied as well as a conflicting Label, then:
+> The continer Label takes priority over the global Natical enviornment variable.
+
## Time Zone
Sets the time-zone to be used by the CRON schedule. If this environment variable is not set, Nautical will use the default time-zone: `Etc/UTC`.
@@ -29,83 +33,103 @@ Tell Nautical to skip backup of containers in this list.
This list can either be the container `name` or full `id`.
-> **Default**: *empty*
+> **Default**: *empty* (no skips)
+
+=== "Example 1"
+ ```properties
+ SKIP_CONTAINERS=container-name1,container-name2,container-name3
+ ```
+
+=== "Example 2"
+ ```properties
+ SKIP_CONTAINERS=container-name1,056bd2e970c1338782733fdbf1009c6e158c715d0d105b11de88bd549430e7f5
+ ```
-```properties
-SKIP_CONTAINERS=container-name1,container-name2,container-name3
-```
-```properties
-SKIP_CONTAINERS=container-name1,056bd2e970c1338782733fdbf1009c6e158c715d0d105b11de88bd549430e7f5
-```
!!! tip "Getting the full container ID"
Usally, it's easier to just use the `container-name`, but if you need to use the full ID, these commands will help:
* `docker ps --no-trunc`
* `docker inspect `
+🔄 This is the same action as the [Skip Containers](./labels.md#skip) label, but applied globally.
+
+## Require Label
+Require the Docker ^^Label^^ `nautical-backup.enable=true` to be present on *each* contianer or it will be skipped.
+
+> **Default**: false
+
+```properties
+REQUIRE_LABEL=true
+```
+
+See the [Labels Enable Section](./labels.md#enable-nautical) for more details.
+
## Override Source Directory
Allows a source directory and container-name that do not match.
-> **Default**: *empty*
+> **Default**: *empty* (use container name)
> **Format**: `:` (comma seperated for multiple items)
Normally a container is backed up *only* when the `container-name` is the exact same as the `source folder name`.
-Example 1:
+=== "Example 1"
-For example, a container named `Pi.Alert` will be skipped with a source directory name of `pialert`.
-To fix this, we can override the source directory name so that it does not need to match the container name.
+ For example, a container named `Pi.Alert` will be skipped with a source directory name of `pialert`.
+ To fix this, we can override the source directory name so that it does not need to match the container name.
-```properties
-OVERRIDE_SOURCE_DIR=Pi.Alert:pialert
-```
+ ```properties
+ OVERRIDE_SOURCE_DIR=Pi.Alert:pialert
+ ```
-Example 2:
+=== "Example 2"
-We can override multiple containers if we seperate them with a comma.
-```properties
-OVERRIDE_SOURCE_DIR=example1:example1-new-source-data,ctr2:ctr2-new-source
-```
- The example above would yield the following results:
+ We can override multiple containers if we seperate them with a comma.
+ ```properties
+ OVERRIDE_SOURCE_DIR=example1:example1-new-source-data,ctr2:ctr2-new-source
+ ```
+ The example above would yield the following results:
-| Container Name | Old Source Directory | New Source Directory |
-| -------------- | -------------------- | ---------------------------- |
-| example1 | `src/example1` | `src/example1-new-dest-data` |
-| ctr2 | `src/ctr2` | `src/newdest` |
+ | Container Name | Old Source Directory | New Source Directory |
+ | -------------- | -------------------- | ---------------------------- |
+ | example1 | `src/example1` | `src/example1-new-dest-data` |
+ | ctr2 | `src/ctr2` | `src/newdest` |
+
+🔄 This is the same action as the [Override Source Directory](./labels.md#override-source-directory-name) label, but applied globally.
## Override Destination Directory
Changes the destination backup name to be something other than the container name.
-> **Default**: *empty*
+> **Default**: *empty* (use container name)
> **Format**: `:` (comma seperated for multiple items)
Normally, a container is backed to a folder with the ^^same name^^ as the `container-name`.
-Example 1:
+=== "Example 1"
-For example, let's say we have a container named `Pi.Alert`. By default, the container will be backed up to a folder named `Pi.Alert`.
-If we want to change this destination folder name to be `pialert`, we can do that using overrides.
+ For example, let's say we have a container named `Pi.Alert`. By default, the container will be backed up to a folder named `Pi.Alert`.
+ If we want to change this destination folder name to be `pialert`, we can do that using overrides.
-```properties
-OVERRIDE_DEST_DIR=Pi.Alert:pialert
-```
+ ```properties
+ OVERRIDE_DEST_DIR=Pi.Alert:pialert
+ ```
-Example 2:
+=== "Example 2"
-```properties
-OVERRIDE_DEST_DIR=example1:example1-new-dest-data,ctr2:newdest
-```
+ ```properties
+ OVERRIDE_DEST_DIR=example1:example1-new-dest-data,ctr2:newdest
+ ```
- The example above would yield the following results:
+ The example above would yield the following results:
-| Container Name | Old Destination Directory | New Destination Directory |
-| -------------- | ------------------------- | ----------------------------- |
-| example1 | `dest/example1` | `dest/example1-new-dest-data` |
-| ctr2 | `dest/ctr2` | `dest/newdest` |
+ | Container Name | Old Destination Directory | New Destination Directory |
+ | -------------- | ------------------------- | ----------------------------- |
+ | example1 | `dest/example1` | `dest/example1-new-dest-data` |
+ | ctr2 | `dest/ctr2` | `dest/newdest` |
+🔄 This is the same action as the [Override Destination Directory](./labels.md#override-destination-directory-name) label, but applied globally.
## Report file
Enable or Disable the automatically generated report file.
@@ -119,7 +143,7 @@ REPORT_FILE=true
## Skip Stopping Containers
Bypass stopping the container before performing a backup. This can be useful for containers with minimal configuration.
-> **Default**: *empty*
+> **Default**: *empty* (no containers will be skipped)
```properties
SKIP_STOPPING=example1,example2
@@ -129,6 +153,9 @@ SKIP_STOPPING=example1,example2
Only do this on containers you know for certain do not need to be shutdown before backup.
+
+🔄 This is the same action as the [Stop Before Backup](./labels.md#stop-before-backup) label, but applied globally.
+
## Backup on Start
Will immediatly perform a backup when the container is started in addition to the CRON sheduled backup.
@@ -167,21 +194,20 @@ USE_DEFAULT_RSYNC_ARGS=false
## Custom rsync Arguments
Apply custom `rsync` args (in addition to the [default](#use-default-rsync-arguments) args)
-> **Default**: *empty*
+> **Default**: *empty* (no custom rsync args will be applied)
The `RSYNC_CUSTOM_ARGS` will be inserted after the `$DEFAULT_RSYNC_ARGS` as shown:
```bash
rsync $DEFAULT_RSYNC_ARGS $RSYNC_CUSTOM_ARGS $src_dir/ $dest_dir/
```
-
There are many `rsync` arguments and customizations that be be used here.
-Examples:
-```properties
-# Don't backup any .log or any .txt files
-RSYNC_CUSTOM_ARGS=--exclude='*.log' --exclude='*.txt'
-```
+=== "Example 1"
+ ```properties
+ # Don't backup any .log or any .txt files
+ RSYNC_CUSTOM_ARGS=--exclude='*.log' --exclude='*.txt'
+ ```
diff --git a/docs/index.md b/docs/index.md
index 2c98fc29..28b3597a 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -7,6 +7,8 @@
+
+
diff --git a/docs/labels.md b/docs/labels.md
new file mode 100644
index 00000000..4bb8fc90
--- /dev/null
+++ b/docs/labels.md
@@ -0,0 +1,134 @@
+Docker Labels allow us to apply settings to Nautical on a per-container basis. Instead of applying [enviornment variables](./arguments.md), we can apply the label to the each container seperately.
+
+### How to add labels
+
+Here are a few examples of how to add labels to a Docker container.
+Remember, these labels can be added to any container (other than Nautical itself).
+
+=== "Docker Compose Example 1"
+ ```yaml
+ version: '3'
+ services:
+ # Service config ...
+ labels:
+ - "nautical-backup.enable=true"
+ - "nautical-backup.stop-before-backup=true"
+ ```
+
+=== "Docker Compose Example 2"
+ ```yaml
+ version: '3'
+ services:
+ pihole:
+ container_name: pihole
+ image: pihole/pihole:latest
+ ports:
+ - "53:53/tcp"
+ - "53:53/udp"
+ - "80:80/tcp"
+ volumes:
+ - './etc-pihole:/etc/pihole'
+ - './etc-dnsmasq.d:/etc/dnsmasq.d'
+ labels:
+ - "nautical-backup.enable=true"
+ - "nautical-backup.stop-before-backup=true"
+ ```
+
+=== "Docker Run Example"
+ ```bash
+ docker run --name example-image \
+ -l nautical-backup.enable=true \
+ -l nautical-backup.stop-before-backup=true \
+ my-image:latest
+ ```
+
+=== "Docker Run Example 2"
+ ```bash
+ docker run -d \
+ --name pihole \
+ -p 53:53/tcp -p 53:53/udp \
+ -p 80:80 \
+ -e TZ="America/Chicago" \
+ -v "${PIHOLE_BASE}/etc-pihole:/etc/pihole" \
+ -v "${PIHOLE_BASE}/etc-dnsmasq.d:/etc/dnsmasq.d" \
+ -l nautical-backup.enable=true \
+ -l nautical-backup.stop-before-backup=true \
+ pihole/pihole:latest
+ ```
+
+### Label vs Enviornment Variable Priority
+If a container has an Enviornment Variable applied as well as a conflicting Label, then:
+> The continer Label takes priority over the global Natical enviornment variable.
+
+## Enable Nautical
+With the [Require Label](./arguments.md#require-label) enviornment variable set to `true`, then all containers will be skipped unless they have this label.
+
+> **Default If Missing**: true
+
+```properties
+nautical-backup.enable=false
+```
+
+If the [Require Label](./arguments.md#require-label) enviornment variable is *missing* or set to `false`, then [this label](#enable-nautical) will not be needed since the container will be backed up anyway.
+
+## Skip
+Skip any containers completely if this label is present.
+
+> **Default If Missing**: false
+
+```properties
+nautical-backup.skip=true
+```
+
+🔄 This is the same action as the [Skip Containers](./arguments.md#skip-containers) variable, but applied only to this container.
+
+## Stop Before Backup
+
+With this label applied, the container will not be stopped before performing a backup.
+
+> **Default If Missing**: false
+
+```properties
+nautical-backup.stop-before-backup=true
+```
+
+!!! warning "Not stoppping containers can produce *corrupt* backups."
+ Containers with databases--particularly SQL--need to be shutdown before backup.
+
+ Only do this on containers you know for certain do not need to be shutdown before backup.
+
+🔄 This is the same action [Skip Stopping Containers](./arguments.md#skip-stopping-containers) variable, but applied only to this container.
+
+## Override Source Directory Name
+
+Changes the source directory name that Nautical will look for.
+
+By default, Nautical will look for the source directory that is the same name as the container name.
+
+> **Default If Missing**: *empty* (use container name)
+
+=== "Example 1"
+ ```properties
+ nautical-backup.override-source-dir=new_folder_name
+ ```
+
+=== "Example 2"
+ To backup the container `Pi.Alert`, the source directory name must be named `Pi.Alert`, but we can use the override to allow a backup of the folder named `pialert`.
+ ```properties
+ nautical-backup.override-source-dir=pialert
+ ```
+
+🔄 This is the same action as the [Override Source Directory](./arguments.md#override-source-directory) variable, but applied only to this container.
+
+## Override Destination Directory Name
+Changes the destination/output directory name that Nautical will create during backups.
+
+By default, Nautical will create destination directory that is the same name as the container name.
+
+> **Default If Missing**: *empty* (use container name)
+
+```properties
+nautical-backup.override-destination-dir=new_folder_name
+```
+
+🔄 This is the same action as the [Override Destination Directory](./arguments.md#override-destination-directory) variable, but applied only to this container.
\ No newline at end of file
diff --git a/docs/todo.md b/docs/todo.md
deleted file mode 100644
index 58cd307d..00000000
--- a/docs/todo.md
+++ /dev/null
@@ -1,9 +0,0 @@
-This project is not complete, and is still under active development. Below are a few examples of things we would like to add:
-
-* [ ] Config file `yml`
- * Used in addition to enviornment variables to configure Nautical.
-* [ ] Backup file exclusions
- * For example, you could exclude `*.log` file from only the Trilium container.
-* [ ] Lifecycle hooks
- * Allow calling a script in the container that will about to be shutdown.
-
diff --git a/mkdocs.yml b/mkdocs.yml
index 90eb9f65..143cb30e 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -35,10 +35,10 @@ nav:
- Home: index.md
- Introduction: introduction.md
- Installation: installation.md
- - Advanced Usage: advanced.md
- Variables: arguments.md
+ - Docker Labels: labels.md
+ - Advanced Usage: advanced.md
- Q & A: q-and-a.md
- - To Do: todo.md
- Developer Documentation:
- Contributing to the Docs: docs.md
diff --git a/pkg/backup.sh b/pkg/backup.sh
index 712318d6..f4e72e35 100644
--- a/pkg/backup.sh
+++ b/pkg/backup.sh
@@ -4,24 +4,23 @@ echo "Starting backup script..."
# Convert the string back to an array
if [ ! -z "$CONTAINER_SKIP_LIST_STR" ]; then
- IFS=',' read -ra SKIP_CONTAINERS <<< "$CONTAINER_SKIP_LIST_STR"
+ IFS=',' read -ra SKIP_CONTAINERS <<<"$CONTAINER_SKIP_LIST_STR"
fi
# Convert the string back to an array
if [ ! -z "$SKIP_STOPPING_STR" ]; then
- IFS=',' read -ra SKIP_STOPPING <<< "$SKIP_STOPPING_STR"
+ IFS=',' read -ra SKIP_STOPPING <<<"$SKIP_STOPPING_STR"
fi
-
# Function to populate override directories
populate_override_dirs() {
- local -n override_dirs_ref=$1 # Use nameref to update the associative array passed as argument
- local override_var=$2 # The environment variable containing the override info
-
+ local -n override_dirs_ref=$1 # Use nameref to update the associative array passed as argument
+ local override_var=$2 # The environment variable containing the override info
+
if [ ! -z "$override_var" ]; then
- IFS=',' read -ra PAIRS <<< "$override_var"
+ IFS=',' read -ra PAIRS <<<"$override_var"
for pair in "${PAIRS[@]}"; do
- IFS=':' read -ra KV <<< "$pair"
+ IFS=':' read -ra KV <<<"$pair"
override_dirs_ref[${KV[0]}]=${KV[1]}
done
fi
@@ -35,7 +34,6 @@ declare -A override_dest_dirs
populate_override_dirs override_source_dirs "$OVERRIDE_SOURCE_DIR"
populate_override_dirs override_dest_dirs "$OVERRIDE_DEST_DIR"
-
# Fetch both container names and IDs
containers=$(docker ps --no-trunc --format="{{.ID}}:{{.Names}}")
number_of_containers=$(echo "$containers" | wc -l)
@@ -52,7 +50,7 @@ report_file="Backup Report - $(date +'%Y-%m-%d').txt"
if [ "$REPORT_FILE" = "true" ]; then
rm -f "$DEST_LOCATION/Backup Report - "*.txt
# Initialize the current report file with a header
- echo "Backup Report - $(date)" > "$DEST_LOCATION/$report_file"
+ echo "Backup Report - $(date)" >"$DEST_LOCATION/$report_file"
fi
default_rsync_args="-ahq"
@@ -66,7 +64,6 @@ if [ ! -z "$RSYNC_CUSTOM_ARGS" ]; then
custom_args="$RSYNC_CUSTOM_ARGS"
fi
-
# Get arguments:
# -s = skips
# -d = override DEST_LOCATION
@@ -85,13 +82,12 @@ containers_completed=0
log_entry() {
local message="$1"
echo "$message"
-
+
if [ "$REPORT_FILE" = "true" ]; then
- echo "$(date) - $message" >> "$DEST_LOCATION/$report_file"
+ echo "$(date) - $message" >>"$DEST_LOCATION/$report_file"
fi
}
-
BackupContainer() {
local container=$1
@@ -99,22 +95,44 @@ BackupContainer() {
for skip in "${SKIP_STOPPING[@]}"; do
if [ "$skip" == "$container" ]; then
skip_stopping=1
+ log_entry "Skipping stopping of $container as it's in the SKIP_STOPPING list."
break
fi
done
+ # Use docker inspect to get the labels for the container
+ labels=$(docker inspect --format '{{json .Config.Labels}}' $id)
+
+ # Check if the label nautical-backup.skip is set to true
+ if echo "$labels" | grep -q '"nautical-backup.stop-before-backup":"false"'; then
+ log_entry "Skipping stopping of $container because of label."
+ skip_stopping=1
+ fi
+
local src_dir="$SOURCE_LOCATION/$container"
if [ ! -z "${override_source_dirs[$container]}" ]; then
src_dir="$SOURCE_LOCATION/${override_source_dirs[$container]}"
log_entry "Overriding source directory for $container to ${override_source_dirs[$container]}"
fi
+ if echo "$labels" | grep -q '"nautical-backup.override-source-dir"'; then
+ new_src_dir=$(echo "$labels" | jq -r '.["nautical-backup.override-source-dir"]')
+ src_dir="$SOURCE_LOCATION/$new_src_dir"
+ log_entry "Overriding source directory for $container to $new_src_dir from label"
+ fi
+
local dest_dir="$DEST_LOCATION/$container"
if [ ! -z "${override_dest_dirs[$container]}" ]; then
dest_dir="$DEST_LOCATION/${override_dest_dirs[$container]}"
log_entry "Overriding destination directory for $container to ${override_dest_dirs[$container]}"
fi
+ if echo "$labels" | grep -q '"nautical-backup.override-destination-dir"'; then
+ new_destination_dir=$(echo "$labels" | jq -r '.["nautical-backup.override-destination-dir"]')
+ dest_dir="$DEST_LOCATION/$new_destination_dir"
+ log_entry "Overriding destination directory for $container to $new_destination_dir from label"
+ fi
+
if [ -d "$src_dir" ]; then
if [ $skip_stopping -eq 0 ]; then
log_entry "Stopping $container..."
@@ -123,8 +141,6 @@ BackupContainer() {
log_entry "Error stopping container $container. Skipping backup for this container."
return
fi
- else
- log_entry "Skipping stopping of $container as it's in the SKIP_STOPPING list."
fi
log_entry "Backing up $container data..."
@@ -153,21 +169,37 @@ BackupContainer() {
fi
}
-
# Loop through all running containers
IFS=$'\n'
for entry in $containers; do
id=${entry%%:*}
name=${entry##*:}
-
skip=0
+
+ if [ "$REQUIRE_LABEL" = "true" ]; then
+ skip=1 # Skip by default unless lable is found
+ fi
+
+ # Use docker inspect to get the labels for the container
+ labels=$(docker inspect --format '{{json .Config.Labels}}' $id)
+
+ if echo "$labels" | grep -q '"nautical-backup.enable":"true"'; then
+ echo "Enabling $name based on label."
+ skip=0
+ fi
+
+ if echo "$labels" | grep -q '"nautical-backup.skip":"true"'; then
+ echo "Skipping $name based on label."
+ skip=1 # Add the container to the skip list
+ fi
+
for cur in "${SKIP_CONTAINERS[@]}"; do
if [ "$cur" == "$name" ]; then
skip=1
echo "Skipping $name based on name."
break
fi
- if [ "$cur" == "$id" ] ; then
+ if [ "$cur" == "$id" ]; then
skip=1
if [ "$cur" == "$SELF_CONTAINER_ID" ]; then
break # Exclude self from logs
@@ -184,4 +216,4 @@ done
containers_skipped=$((number_of_containers - containers_completed))
-echo "Success. $containers_completed containers backed up! $containers_skipped skipped."
\ No newline at end of file
+echo "Success. $containers_completed containers backed up! $containers_skipped skipped."
diff --git a/pkg/entry.sh b/pkg/entry.sh
index c4794403..cc2485c5 100644
--- a/pkg/entry.sh
+++ b/pkg/entry.sh
@@ -118,6 +118,9 @@ if [ "$USE_DEFAULT_RSYNC_ARGS" = "false" ]; then
echo "USE_DEFAULT_RSYNC_ARGS: $USE_DEFAULT_RSYNC_ARGS"
fi
+if [ "$REQUIRE_LABEL" = "true" ]; then
+ echo "REQUIRE_LABEL: $REQUIRE_LABEL"
+fi
if [ "$BACKUP_ON_START" = "true" ]; then
echo "BACKUP_ON_START: $BACKUP_ON_START"