IPv4 & IPv6 Leasing - Any RIR, Any LocationOrder Now
Hostperl

Git-Based Docker Deployments on VPS for Hostperl Users

By Raman Kumar

Share:

Updated on Aug 13, 2026

Git-Based Docker Deployments on VPS for Hostperl Users

Use Git as the deployment source, not the server clipboard

Git-based Docker deployments on VPS work better than one-off file uploads when you need repeatable launches, fast rollbacks, and a migration path you can trust. If you run client sites, internal tools, or small SaaS apps on Hostperl, you already know the common failure points: a build that works on one machine but not another, an environment variable that disappears after reboot, or a deployment nobody can reproduce six weeks later. This guide walks you from the first SSH login to a working Docker Compose setup with Nginx in front, systemd keeping it alive, and a rollback path that does not depend on guesswork.

If you are choosing a VPS for this kind of work, Hostperl VPS gives you the control you need for Docker, reverse proxies, and app isolation without forcing you into a panel workflow that does not fit your stack.

This tutorial uses one documentation IP, 203.0.113.10, for every command example. Replace it with the real public IP assigned to your Hostperl server.

Connect, identify the OS, and create a sudo user

On your local computer:

ssh root@203.0.113.10

That address is reserved for documentation. Use your server’s real IP instead. Keep the root session open until the new user login works.

If your provider gave you a custom SSH port, the first connection would look like this:

ssh -p 2222 root@203.0.113.10

Now check the operating system before you install anything.

On the VPS as root:

cat /etc/os-release

You should see either Ubuntu/Debian or AlmaLinux/Rocky Linux. The next steps differ slightly.

Ubuntu and Debian

On the VPS as root:

apt update
apt install -y sudo adduser openssh-server ca-certificates curl gnupg

Create the non-root admin account and add it to the sudo group.

adduser deploy
usermod -aG sudo deploy

Set up SSH keys for the new account. Replace the key content with your own public key.

mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
cat > /home/deploy/.ssh/authorized_keys <<'EOF'
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyChangeThisForYourServer deploy@example
EOF
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh

Open a second terminal now and test the new login before you change any root access settings.

ssh deploy@203.0.113.10
sudo -v

You should log in cleanly and get a sudo password prompt or confirmation. Do not continue until that works.

AlmaLinux and Rocky Linux

On the VPS as root:

dnf install -y sudo shadow-utils openssh-server ca-certificates curl

Create the non-root admin account and grant wheel access.

useradd -m -s /bin/bash deploy
passwd deploy
usermod -aG wheel deploy

Set up SSH keys safely.

mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
cat > /home/deploy/.ssh/authorized_keys <<'EOF'
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyChangeThisForYourServer deploy@example
EOF
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh

Test from a second terminal before you touch root login settings.

ssh deploy@203.0.113.10
sudo -v

For a Hostperl-managed VPS, this is also the point where support tickets get easier. Once you have a known admin user and key-based SSH, migrations and app cutovers become much cleaner.

Install Docker and Docker Compose cleanly

Use current Docker packages from Docker’s repository instead of older distro builds. That keeps the Engine and Compose plugin aligned.

Ubuntu and Debian

On the VPS as the non-root sudo user:

sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
. /etc/os-release
printf 'deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu %s stable\n' "$VERSION_CODENAME" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Check the version and enable Docker at boot.

docker --version
docker compose version
sudo systemctl enable --now docker

AlmaLinux and Rocky Linux

On the VPS as the non-root sudo user:

sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

Confirm the install.

docker --version
docker compose version
sudo systemctl status docker --no-pager

If you want a deeper look at safe container checks after deployment, Hostperl has a related guide on Docker health checks for VPS deployments in 2026.

Prepare the application directory and clone the repo

Use one directory for the app, one for persistent data, and one for logs. That keeps restores much simpler.

On the VPS as the non-root sudo user:

sudo mkdir -p /opt/myapp/{app,data,logs}
sudo chown -R deploy:deploy /opt/myapp
cd /opt/myapp
pwd

Clone your Git repository. Replace the example URL with your real repository.

git clone https://github.com/example/example-app.git app
cd /opt/myapp/app
git rev-parse --short HEAD

You should see a commit hash. That gives you a clear deployment anchor for rollbacks.

Write the Docker Compose stack and environment file

This example runs a simple app container on port 8000 behind Nginx. Change the image name and ports to match your application, but keep the structure.

On the VPS as the non-root sudo user:

cd /opt/myapp/app
nano compose.yaml

Paste this content, then save and exit with Ctrl+O, Enter, and Ctrl+X.

services:
  web:
    image: ghcr.io/example/example-app:latest
    container_name: example-app-web
    restart: unless-stopped
    env_file:
      - /opt/myapp/app/.env
    ports:
      - "127.0.0.1:8000:8000"
    volumes:
      - /opt/myapp/data:/data
      - /opt/myapp/logs:/var/log/example-app
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

Create the environment file with restrictive permissions.

nano /opt/myapp/app/.env
chmod 600 /opt/myapp/app/.env

Example contents:

APP_ENV=production
APP_PORT=8000
APP_SECRET=change-this-secret

That secret file should stay owned by deploy and readable only by that user.

Run the stack and lock it into systemd

Start the app through Compose first. If the image or env file is wrong, you want the failure to happen here, not after a reboot.

On the VPS as the non-root sudo user:

cd /opt/myapp/app
docker compose pull
docker compose up -d
docker compose ps

You should see the service in a healthy or running state. If the app exposes a health endpoint, check it from inside the VPS.

curl -i http://127.0.0.1:8000/health

Now create a systemd unit so Docker Compose starts at boot in a predictable way.

sudo nano /etc/systemd/system/myapp-compose.service

Paste this file and save it.

[Unit]
Description=MyApp Docker Compose Service
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp/app
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target

Test the unit before you enable it.

sudo systemctl daemon-reload
sudo systemctl start myapp-compose.service
sudo systemctl status myapp-compose.service --no-pager
sudo systemctl enable myapp-compose.service

If you want a Git-first workflow with stronger change control, Hostperl also publishes Docker app deployment on VPS and Git-based Docker app deployment on VPS in 2026.

Put Nginx in front and open the firewall

Nginx listens on 80 and 443, then forwards requests to the container on 127.0.0.1:8000. That keeps the app private on the server.

Ubuntu and Debian

On the VPS as the non-root sudo user:

sudo apt install -y nginx
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw --force enable

Create the site file.

sudo nano /etc/nginx/sites-available/myapp

Use this configuration, then save and exit.

server {
    listen 80;
    server_name server.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        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;
    }
}

Enable the site and test syntax before you reload.

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager

AlmaLinux and Rocky Linux

On the VPS as the non-root sudo user:

sudo dnf install -y nginx firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
sudo systemctl enable --now nginx

Create the Nginx server block.

sudo nano /etc/nginx/conf.d/myapp.conf

Use this file content.

server {
    listen 80;
    server_name server.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        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;
    }
}

Check syntax and reload.

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager

If you need a broader comparison of web server choices for VPS workloads, see Nginx vs Apache for VPS hosting in 2026.

Add HTTPS with Let’s Encrypt

Once DNS points to the VPS, request a certificate. Replace server.example.com with your real hostname and make sure the DNS A record already resolves to the server.

Ubuntu and Debian

On the VPS as the non-root sudo user:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d server.example.com

Certbot will update the Nginx config and ask whether to redirect HTTP to HTTPS. Choose redirect if this is the public site.

AlmaLinux and Rocky Linux

On the VPS as the non-root sudo user:

sudo dnf install -y certbot python3-certbot-nginx
sudo certbot --nginx -d server.example.com

Confirm automatic renewal is scheduled.

sudo systemctl list-timers | grep certbot || true
sudo certbot renew --dry-run

Verify the deployment from server and client

Do not stop at “the container is running.” Verify the full chain: Docker, systemd, Nginx, HTTP, and a reboot.

On the VPS as the non-root sudo user:

docker ps
sudo systemctl status myapp-compose.service --no-pager
sudo ss -ltnp | grep -E '(:80|:443|:8000)'
journalctl -u myapp-compose.service -n 50 --no-pager
journalctl -u nginx -n 50 --no-pager

On your local computer:

curl -I https://server.example.com
curl https://server.example.com/health

You should see a successful HTTP response and the app’s health payload or status code.

On the VPS as root, only after the new login is verified and the site works:

reboot

After the server returns, reconnect and confirm everything survived the restart.

ssh deploy@203.0.113.10
docker ps
systemctl status docker myapp-compose nginx --no-pager

Rollback, troubleshooting, and common failures

Most deployment problems fall into a few predictable buckets. Check the command that matches the symptom.

  • Nginx fails to reload: run sudo nginx -t. If it reports a syntax error, open the file named in the error, fix the block, then run sudo systemctl reload nginx.
  • The container exits immediately: run docker compose logs --tail=100 inside /opt/myapp/app. Missing environment values and bad image names are the usual causes.
  • Port 8000 is not listening: run docker ps and sudo ss -ltnp | grep 8000. If nothing is listening, the app never started or mapped a different internal port.
  • HTTPS certificate request fails: run sudo certbot certificates and confirm that DNS points to this server and port 80 is open.
  • App works locally but not from the browser: check curl -I http://127.0.0.1:8000 on the VPS, then inspect the firewall with sudo ufw status or sudo firewall-cmd --list-all.

For rollback, redeploy the earlier Git commit and restart the stack.

cd /opt/myapp/app
git log --oneline -n 5
git checkout <previous-commit-hash>
docker compose up -d --force-recreate
sudo systemctl restart myapp-compose.service

Replace <previous-commit-hash> with a real commit from git log --oneline. That gives you a deterministic fallback without rebuilding the whole server.

Keep the deployment maintainable

Once the first release is live, keep the workflow simple. Update the base system regularly, store secrets outside the image, and make every release point to a Git commit you can explain to a customer or teammate. That is the difference between a one-off container demo and a deployment you can support through migrations, incidents, and growth.

Hostperl customers who want a clean path from Git to production usually do best on a VPS with enough headroom for Docker, logs, and reverse proxy traffic. If you need a server for repeated launches or client handoffs, start with Hostperl VPS hosting and keep the workflow simple.

For teams that expect more than one service, build in time for a proper deployment check and health monitoring. Hostperl’s support team can help you choose the right size before you push the first container.

FAQ

Do I need Docker Compose v2 for this?

Yes. This tutorial uses docker compose, which is the current Compose plugin flow. It is easier to support and more predictable across restarts.

Should my app listen on 0.0.0.0 or 127.0.0.1?

Use 127.0.0.1 on the host mapping in this setup. Nginx should be the public entry point, not the app container.

Can I deploy from GitHub Actions or another CI system later?

Yes. Start with a manual Git pull and Compose restart, then automate only after you know the rollback path works.

What if I am migrating from a panel or older VM?

Keep the old server live until the new stack passes HTTP, HTTPS, login, and reboot checks. Hostperl’s migration-friendly VPS workflow makes that cutover much easier.