The Blog of Joshua Blais.

How I Deploy my apps with one command

Cover Image for How I Deploy my apps with one command
Joshua Blais
Joshua Blais

Instead of using actions or some other way of CI/CD’ing my apps, I simply work with a deploy script that will build a Docker Container, push it to my registry, SSH into the remote, pull the container, make sure it runs, then apply NGINX configurations and SSL certificates so that the app is live on the internet for all to see.

This means I don't need any CI/CD additions generally, and I can create Dockefiles or Docker-composes for the purpose of each apps deployment. Easy.

It’s how I publish blog posts, deploy my apps, and get things out there as fast as possible.

Steal the script and go from there:

#!/bin/bash

# Check the number of arguments
if [ "$#" -ne 3 ]; then
echo "Usage: $0 <image_name> <port_mapping> <domain>"
exit 1
fi

# Assign arguments to variables
IMAGE_NAME=$1
PORT_MAPPING=$2
DOMAIN=$3
EMAIL=""
TAG="latest"  # Adjust tagging strategy as needed
DOCKER_REGISTRY=""  # Docker registry URL
SERVER_HOST=""
SSH_USER=""  # SSH user on the remote server
SSH_KEY_PATH=""  # SSH private key path

# Extract host port from PORT_MAPPING
HOST_PORT=$(echo $PORT_MAPPING | cut -d':' -f1)

# Build the Docker image with Buildx
docker buildx build --platform linux/amd64 -t $DOCKER_REGISTRY/"$IMAGE_NAME":$TAG --load .

# Push the Docker image
docker push $DOCKER_REGISTRY/$IMAGE_NAME:$TAG

# SSH into server to pull the image, restart the container, and configure NGINX and Certbot
ssh -i $SSH_KEY_PATH $SSH_USER@$SERVER_HOST << EOF
# Pull the latest Docker image
docker pull $DOCKER_REGISTRY/$IMAGE_NAME:$TAG

# Stop and remove the existing container if it exists
docker stop $IMAGE_NAME || true
docker rm $IMAGE_NAME || true

# Run the new container in the background with the specified port mapping
docker run -d --name $IMAGE_NAME -p $PORT_MAPPING $DOCKER_REGISTRY/$IMAGE_NAME:$TAG

# Check if NGINX config exists, if not, create it
NGINX_CONFIG="/etc/nginx/sites-available/$DOMAIN.conf"
NGINX_ENABLED="/etc/nginx/sites-enabled/$DOMAIN.conf"

if [ ! -f "\$NGINX_CONFIG" ]; then
    sudo bash -c "cat > \$NGINX_CONFIG" << 'ENDOFFILE'
server {
    listen 80;
    server_name $DOMAIN;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://\$host\$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name $DOMAIN;

    ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;

    location / {
        proxy_pass http://localhost:$HOST_PORT;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
    }
}
ENDOFFILE

    sudo ln -s "\$NGINX_CONFIG" "\$NGINX_ENABLED"
fi

# Reload NGINX to apply configuration
sudo nginx -t && sudo systemctl reload nginx

# After NGINX is reloaded:
sudo certbot certonly --webroot -w /var/www/certbot -d $DOMAIN --email $EMAIL --agree-tos --non-interactive --deploy-hook "sudo systemctl reload nginx"

# Reload NGINX to use new SSL certificate
sudo systemctl reload nginx
EOF

echo "Deployment complete."

You can then use

deploy app-name "port:port" appdomain.com

and the docker container will build, push to your registry, pull down to your server, and deploy itself to a domain of your choosing.

Nice.

Subscribe for updates direct to your inbox.