On-Prem Docker Deployment — Step-by-step
On-Prem Docker Deployment — Step-by-step
Author- Vikramsinh Shinde
date - 2-Dec-2025
Audience: Developers / DevOps engineers with basic Linux and Docker knowledge.
Objective: Deploy a simple web service using Docker and Docker Compose on an on-prem Linux server and document every step used in blog/tutorial.
Table of Contents
-
Introduction
-
Prerequisites
-
Project structure
-
Create the custom NGINX Docker image
-
index.html -
Dockerfile -
Build & verify
-
-
Create
docker-compose.yml-
Explanation of options used
-
Start the stack
-
-
Create systemd service to manage docker-compose
-
Why systemd
-
Service file content
-
Enable & start
-
-
Configure log rotation for container logs
-
Why
-
Logrotate config
-
Test
-
-
Testing the service
-
curl -
docker psand logs
-
-
Troubleshooting
-
Common errors & fixes
-
-
Lesson Learned
-
Conclusion & next steps
1. Introduction
This guide documents an on-prem deployment of a simple NGINX web service using Docker and Docker Compose. It includes file contents, commands to run, systemd integration to manage the Compose stack, log rotation for container logs, and tests.
2. Prerequisites
-
A Linux machine (Ubuntu/Debian/CentOS) with root or sudo access.
-
Docker installed and functional.
-
Docker Compose installed (
/usr/bin/docker-composeor Compose v2 plugin). -
curlfor testing. -
Basic understanding of systemd for service management.
Example working directory used in this guide:
/on-prem
3. Project structure
root@on-prem-server:/on-prem# tree
.
├── docker-compose.yml
├── dockerfile
├── html
│ └── index.html
└── logsCreate the folder and move into it:
sudo mkdir -p /on-prem/html /on-prem/logs
cd /on-prem
sudo chown $USER:$USER /on-prem
4. Create the custom NGINX Docker image
html/index.html
Create a simple HTML page that confirms the on-prem deployment:
<!DOCTYPE html>
<html>
<head>
<title>On-Prem Docker Deployment</title>
</head>
<body>
<h1>Hello from On-Prem NGINX Docker!</h1>
<p>Deployed using Docker Compose and managed by systemd.</p>
</body>
</html>
Save it to /on-prem/html/index.html.
Dockerfile
Create a Dockerfile that uses the official NGINX image and copies the custom HTML:
FROM nginx:latest
# Remove default HTML
RUN rm -rf /usr/share/nginx/html/*
# Copy custom content
COPY html/ /usr/share/nginx/html/
# Expose container port
EXPOSE 80
Build & Verify image (optional manual build)
cd /on-prem
sudo docker-compose build
# or
sudo docker build -t on-prem_web .
# Verify
sudo docker images | grep on-prem_web
5. Create docker-compose.yml
Create /on-prem/docker-compose.yml:
version: '3.9'
services:
web:
build: .
container_name: onprem-nginx
ports:
- "8080:80"
restart: always
volumes:
- ./logs:/var/log/nginx
Notes:
-
restart: alwaysensures the container restarts after host reboots. -
./logsis bind-mounted so logs are available on the host and manageable by logrotate.
Start the stack:
cd /on-prem
sudo docker-compose up -d
sudo docker ps
6. Create systemd service to manage docker-compose
Why systemd?
Using systemd allows the Compose stack to be started/stopped as a native service and enables auto-start at boot.
Service file: /etc/systemd/system/onprem-nginx.service
[Unit]
Description=On-Prem NGINX Docker Compose Service
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
WorkingDirectory=/on-prem
ExecStart=/usr/bin/docker-compose up -d
ExecStop=/usr/bin/docker-compose down
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
If your system uses the Compose v2 plugin use
/usr/bin/docker compose up -dinstead.
Reload systemd and enable the service:
sudo systemctl daemon-reload
sudo systemctl enable onprem-nginx
sudo systemctl start onprem-nginx
sudo systemctl status onprem-nginx
You should see Active: active (exited) or active (running) depending on the Type used.
7. Configure log rotation for container logs
Because logs are bind-mounted to /on-prem/logs, use logrotate to manage them.
Create /etc/logrotate.d/onprem-nginx:
/on-prem/logs/*.log {
daily
rotate 7
compress
missingok
notifempty
copytruncate
}
Notes: copytruncate is used because Docker can hold file descriptors open. Alternatively, the logging driver (like json-file) has its own rotation options.
Test logrotate configuration:
sudo logrotate -d /etc/logrotate.d/onprem-nginx
Run for real (careful):
sudo logrotate -f /etc/logrotate.d/onprem-nginx
8. Testing the service
Run these checks and capture outputs for your blog:
# Check containers
sudo docker ps
# Curl the service locally
curl http://localhost:8080
# From remote machine, use server IP
curl http://<server-ip>:8080
Expected curl output snippet:
<!DOCTYPE html>
<html>
...Hello from On-Prem NGINX Docker!
Check systemd status:
sudo systemctl status onprem-nginx
Check logs directory:
ls -la /on-prem/logs
tail -n 50 /on-prem/logs/access.log
9. Troubleshooting
systemd: status=203/EXEC — means the ExecStart points to a binary that does not exist. Fix by pointing to the correct path returned by which docker-compose or use the docker compose plugin path.
Connection refused when curling — ensure container is running (docker ps), firewall allows port 8080, and docker-compose.yml maps port 8080:80.
SSH / MobaXterm issues — verify server is up, SSH service running (sudo systemctl status ssh), and you are using correct IP/username/key.
Docker build fails — check Dockerfile syntax, network access for pulling base images, and disk space.
10. Lessons Learned
Always verify the path of
docker-composewhen using systemd to avoidstatus=203/EXECerrors.Bind-mounting logs makes it easier to manage container logs on the host and integrate with
logrotate.Testing each component step-by-step prevents cascading failures and simplifies troubleshooting.
Using
restart: alwaysensures high availability for on-prem deployments.Systemd integration provides a native way to manage Docker Compose stacks, including auto-start on boot.
11. Conclusion & next steps
You now have a reproducible on-prem Docker deployment managed by systemd with log rotation. Next steps you might document in a follow-up post:
-
Use Docker labels or environment variables for configuration.
-
Move logs to a central logging system (ELK / Grafana).
-
Add TLS termination (Let's Encrypt or a reverse proxy).
-
Add health checks and monitoring.


Comments
Post a Comment