I've prepared a quick one using reverse port forwarding and a local temp registry. In case anyone finds it useful:
#!/bin/bash
set -euo pipefail
IMAGE_NAME="my-app"
IMAGE_TAG="latest"
# A temporary Docker registry that runs on your local machine during deployment.
LOCAL_REGISTRY="localhost:5000"
REMOTE_IMAGE_NAME="${LOCAL_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
REGISTRY_CONTAINER_NAME="temp-deploy-registry"
# SSH connection details.
# The jump host is an intermediary server. Remove `-J "${JUMP_HOST}"` if not needed.
JUMP_HOST="user@jump-host.example.com"
PROD_HOST="user@production-server.internal"
PROD_PORT="22" # Standard SSH port
# --- Script Logic ---
# Cleanup function to remove the temporary registry container on exit.
cleanup() {
echo "Cleaning up temporary Docker registry container..."
docker stop "${REGISTRY_CONTAINER_NAME}" >/dev/null 2>&1 || true
docker rm "${REGISTRY_CONTAINER_NAME}" >/dev/null 2>&1 || true
echo "Cleanup complete."
}
# Run cleanup on any script exit.
trap cleanup EXIT
# Start the temporary Docker registry.
echo "Starting temporary Docker registry..."
docker run -d -p 5000:5000 --name "${REGISTRY_CONTAINER_NAME}" registry:2
sleep 3 # Give the registry a moment to start.
# Step 1: Tag and push the image to the local registry.
echo "Tagging and pushing image to local registry..."
docker tag "${IMAGE_NAME}:${IMAGE_TAG}" "${REMOTE_IMAGE_NAME}"
docker push "${REMOTE_IMAGE_NAME}"
# Step 2: Connect to the production server and deploy.
# The `-R` flag creates a reverse SSH tunnel, allowing the remote host
# to connect back to `localhost:5000` on your machine.
echo "Executing deployment command on production server..."
ssh -J "${JUMP_HOST}" "${PROD_HOST}" -p "${PROD_PORT}" -R 5000:localhost:5000 \
"docker pull ${REMOTE_IMAGE_NAME} && \
docker tag ${REMOTE_IMAGE_NAME} ${IMAGE_NAME}:${IMAGE_TAG} && \
systemctl restart ${IMAGE_NAME} && \
docker system prune --force"
echo "Deployment finished successfully."