revs412@portfolio:~/notes/docker-deployment-scripts-for-small-services$cat docker-deployment-scripts-for-small-services.md

Why This Note Exists

Running a service with Docker is simple the first time.

Maintaining it becomes annoying when every update requires remembering the exact build command, container name, environment file, network mode, restart policy, logs command, and cleanup steps.

This note documents the practical direction for creating small helper scripts around Docker services.

The goal is not to hide Docker completely. The goal is to make common operations repeatable so deployment becomes less error-prone.

Context

This kind of scripting is useful for small services such as:

  • bots
  • lightweight APIs
  • internal tools
  • homelab services
  • small automation workers
  • personal utilities
  • services deployed on a VPS or OpenWrt-capable device

The service may be simple, but the operational steps still matter.

A bad update process can create issues like:

  • old containers still running
  • new image built but not used
  • missing environment variables
  • wrong container name
  • duplicate containers
  • logs hard to find
  • no clean restart path
  • forgotten cleanup commands

Helper scripts reduce that friction.

What This Setup Is Meant To Prove

  • deployment should be repeatable, not based on memory
  • small services still need start, stop, restart, logs, and cleanup flows
  • scripts can reduce mistakes without introducing a full CI/CD platform
  • environment files should be handled consistently
  • Docker commands should be wrapped only where it improves reliability
  • service updates should be easy to test and roll back mentally
  • good local tooling matters even for small personal or freelance projects

Tools and Areas Used

Docker Layer

  • Dockerfile
  • Docker image build
  • container run
  • container stop/remove
  • restart policy
  • environment file loading
  • logs
  • container status
  • image cleanup

Script Layer

  • shell scripts
  • help command
  • argument handling
  • status output
  • safe stop/restart flow
  • consistent naming
  • one-command deployment actions

Service Layer

  • small Node.js service
  • bot or worker process
  • environment variables
  • persistent configuration
  • logs and error output
  • service verification after restart

Intended Build

The intended result is a small deployment helper script or script folder that provides common service operations.

A finished setup should allow:

  • build the image
  • run the service
  • stop the service
  • restart the service
  • view logs
  • show status
  • clean old containers/images where appropriate
  • print help
  • avoid typing long Docker commands manually
  • keep naming consistent across updates

The helper should make the service easier to maintain without becoming a large deployment framework.

A practical script should support commands like:

help
build
run
stop
restart
logs
status
clean
rebuild

The exact command names can change, but the purpose should stay clear.

Example Project Shape

A simple service folder can look like this:

service-name/
  Dockerfile
  package.json
  src/
  .env.local
  .env.example
  scripts/
    service.sh

Or, for a smaller project:

service-name/
  Dockerfile
  .env.local
  deploy.sh

The important part is that the script lives near the service and documents the expected workflow.

Environment File Handling

A helper script should use one predictable environment file.

Example:

.env.local

The script should check that it exists before running the container.

Example behavior:

if .env.local is missing:
  print a clear error
  stop before running the service

This is better than starting a broken container that exits immediately because the token, API key, or config value is missing.

Container Naming

The script should define a clear container name.

Example:

CONTAINER_NAME="service-name"
IMAGE_NAME="service-name:latest"

This avoids random Docker-generated names and makes logs/status/restart commands predictable.

Without consistent names, maintenance becomes harder.

Build Command

The build command should create or update the Docker image.

Example direction:

docker build -t service-name:latest .

A helper script should print what it is doing:

[+] Building service-name:latest

This makes script output easier to read.

Run Command

The run command should start a new container with the correct configuration.

Common options:

--name
--env-file
--restart unless-stopped
--network
-d

For a small outbound-only bot or worker, public ports may not be needed.

Example direction:

docker run -d --name service-name --env-file .env.local --restart unless-stopped service-name:latest

If the service requires host networking, the script should make that explicit.

Stop Command

The stop command should stop and remove the running container cleanly.

Example direction:

docker stop service-name
docker rm service-name

The script should not fail noisily if the container does not exist. It can print a clear message instead.

Restart Command

The restart command should be predictable.

Two common meanings exist:

Restart existing container

docker restart service-name

This is fast, but it does not rebuild or use new code.

Recreate container from current image

stop → run

This is useful after rebuilding the image.

The script should make the behavior clear.

For code updates, the safer flow is usually:

build → stop → run → logs/status

Rebuild Command

A useful command is:

rebuild

Meaning:

build image
stop old container
run new container
show logs/status

This is the command used after changing code.

It avoids the common mistake of building an image but forgetting to recreate the container.

Logs Command

Logs should be easy to reach.

Example direction:

docker logs -f service-name

A helper command like this is useful:

./deploy.sh logs

Logs are the first place to check after a restart.

Status Command

Status should show whether the container is running.

Example direction:

docker ps -a --filter name=service-name

A good script can also show:

  • container name
  • image name
  • running/exited state
  • restart count if available
  • recent logs if useful

Clean Command

Cleanup should be careful.

A clean command can remove old stopped containers or unused images, but it should not delete unrelated Docker resources without warning.

Good cleanup behavior:

  • remove this service’s stopped container
  • remove this service’s old image if intended
  • avoid global destructive cleanup by default

Avoid making clean run something dangerous like:

docker system prune -a

unless the script clearly asks for confirmation.

Example Script Direction

This is a simplified example shape:

#!/bin/sh

APP_NAME="service-name"
IMAGE_NAME="$APP_NAME:latest"
CONTAINER_NAME="$APP_NAME"
ENV_FILE=".env.local"

case "$1" in
  build)
    docker build -t "$IMAGE_NAME" .
    ;;

  run)
    if [ ! -f "$ENV_FILE" ]; then
      echo "Missing $ENV_FILE"
      exit 1
    fi

    docker run -d       --name "$CONTAINER_NAME"       --env-file "$ENV_FILE"       --restart unless-stopped       "$IMAGE_NAME"
    ;;

  stop)
    docker stop "$CONTAINER_NAME" 2>/dev/null || true
    docker rm "$CONTAINER_NAME" 2>/dev/null || true
    ;;

  restart)
    "$0" stop
    "$0" run
    ;;

  rebuild)
    "$0" build
    "$0" stop
    "$0" run
    "$0" logs
    ;;

  logs)
    docker logs -f "$CONTAINER_NAME"
    ;;

  status)
    docker ps -a --filter "name=$CONTAINER_NAME"
    ;;

  help|*)
    echo "Usage: $0 {build|run|stop|restart|rebuild|logs|status|help}"
    ;;
esac

This is not meant to be universal. It is a starting pattern that should be adapted to each service.

Practical Decisions

Keep commands small and obvious

A deployment script should be boring.

If the script becomes too clever, it becomes another thing to debug.

Make rebuild different from restart

Restarting a container does not apply new code if the image was rebuilt but the container was not recreated.

A separate rebuild command makes this clearer.

Check for env file before running

Fail early if required configuration is missing.

This avoids containers exiting with unclear errors.

Avoid global cleanup by default

Cleanup should target the service unless the user explicitly asks for a full Docker cleanup.

This protects other containers on the same machine.

A few simple messages make script output much easier to understand.

Example:

[+] Building image
[+] Stopping old container
[+] Starting new container
[+] Showing logs

Keep one main deployment path

Avoid having multiple conflicting ways to run the same service.

If Docker is the deployment path, the script should be the normal way to manage it.

Common Failure Points

Image Built but Old Code Still Runs

Cause:

docker build was run, but the old container was not recreated

Fix:

build → stop → run

or:

rebuild

Container Name Already Exists

Cause:

old stopped container still exists

Fix:

docker rm service-name

A good stop command should handle this.

Missing Environment Variables

Symptoms:

  • service exits immediately
  • token/API errors
  • config undefined
  • bot does not login
  • API server fails on startup

Fix:

  • check .env.local
  • check --env-file
  • check variable names
  • check application config loading

Logs Show Nothing Useful

Causes:

  • app does not log startup errors
  • process exits before logging
  • logs checked for wrong container
  • container name mismatch
  • service redirects logs somewhere else

Fix:

  • use consistent container names
  • log startup config without secrets
  • check docker ps -a
  • check docker logs

Docker Architecture Issues

On ARM devices, the image architecture matters.

Symptoms:

  • exec format error
  • container exits immediately
  • binary cannot run

Fix direction:

  • build for the target architecture
  • use base images that support the device
  • avoid forcing the wrong platform unless intentionally emulating

Storage Filling Up

Docker images and containers can take space over time.

Useful checks:

docker images
docker ps -a
docker system df

Cleanup should be done carefully.

What A Finished Setup Should Show

A strong finished setup should show:

  • Dockerfile present
  • .env.example documented
  • .env.local used locally but not committed
  • helper script with help output
  • build command works
  • run command works
  • restart/rebuild behavior clear
  • logs command works
  • status command works
  • cleanup command is safe
  • README explains normal workflow
  • service survives reboot if restart policy is used
  • no manual long command required for common actions

Evidence Worth Capturing

Useful evidence for this note would include:

  • script file
  • help output
  • Dockerfile
  • .env.example
  • build output
  • docker ps output
  • logs output
  • rebuild flow screenshot
  • failed container example and fix
  • README deployment section
  • notes about architecture/platform if running on ARM

Technical Assumptions

This setup assumes the service is small enough to run comfortably on the target machine.

It also assumes that Docker is already installed and working.

The script does not replace understanding Docker. It only makes repeated operations consistent.

Key Risks

  • hiding too much Docker behavior behind a script
  • script deleting unrelated containers or images
  • environment file accidentally committed
  • old container using old code
  • container name mismatch
  • missing logs
  • no rollback plan
  • architecture mismatch on ARM devices
  • disk usage growing over time
  • script becoming more complicated than the service

Current State

This note represents the deployment maintenance pattern used for small services.

The value is in creating a simple operational loop:

edit → build → stop old container → run new container → check logs

That loop is enough for many small services without needing a full CI/CD system.

What This Note Does Not Claim

This note does not claim that helper scripts replace proper deployment platforms.

It does not claim that Docker is required for every small service.

It does not claim that this is a production-grade orchestration system.

It is a practical pattern for keeping small Docker services maintainable.

Practical Takeaway

A Docker deployment script is useful when it prevents repeated mistakes.

The useful parts are:

  • consistent image name
  • consistent container name
  • predictable env file
  • one command for rebuilds
  • easy logs
  • safe stop/remove flow
  • clear help output
  • no unnecessary magic

That is enough to make small service deployment much less annoying.