When a company mailbox stops working, the root cause almost always falls into one of…
Deploy Docker Application on Linux VPS: Step-by-Step Guide

Deploying an application on a Linux VPS gives developers more control over their runtime environment, dependencies, networking, and deployment process. Docker makes this easier by packaging an application and its dependencies into a container that can run consistently across supported environments.
Instead of manually installing every application dependency on the server, you can build or download a Docker image and run it inside an isolated container. This approach is useful for web applications, APIs, development tools, and many self-hosted services.
In this guide, you will learn how to deploy a Docker application on a Linux VPS, install Docker Engine, create a sample web application, configure Docker Compose, expose the application through a port, and troubleshoot common deployment problems.
The examples use Ubuntu or Debian with a public IPv4 address and SSH access. Commands should be adjusted if your server uses a different operating system or network configuration.
What Is Docker?
Docker is a platform for building, distributing, and running applications in containers. A container packages an application with its required libraries and runtime dependencies while sharing the host operating system’s kernel.
Several Docker components are useful to understand:
- Docker Engine: Runs and manages containers on the server.
- Docker image: A packaged template used to create containers.
- Docker container: A running instance of an image.
- Dockerfile: Instructions for building a custom image.
- Docker Compose: A tool for defining and managing applications using a YAML configuration file.
For example, a Node.js application may require a particular Node.js version and several npm packages. A Docker image can package those requirements so that the application runs in a consistent environment without installing the same application dependencies directly on the VPS.
Why Deploy Docker Applications on a VPS?
A VPS provides dedicated virtualized resources and administrative control over an operating system. Docker adds a convenient way to run and manage applications on that server.
Some practical benefits include:
- Consistent environments: Reduce differences between development and production.
- Simplified deployment: Start an application from an existing image or build one from source.
- Process isolation: Separate application processes and filesystems, subject to the container’s configuration.
- Easier updates: Replace a container with a newer image when deploying an updated application.
- Multiple applications: Run different applications on the same server when resources and networking permit.
- Reproducible configuration: Store application definitions in version-controlled Compose files.
Containers are not equivalent to virtual machines, and they do not provide a complete security boundary by default. Applications still require secure configuration, patching, access control, and backups.
Prerequisites
Before deploying your first Docker application, prepare the following:
| Requirement | Purpose |
|---|---|
| Linux VPS | Runs Docker Engine and the application |
| Ubuntu or Debian | Operating system for this tutorial |
| SSH access | Allows remote administration |
| Sudo privileges | Required for installing and configuring Docker |
| Public IP address | Allows external users to reach the application |
| Domain name (optional) | Provides a human-readable application address |
| Firewall access | Controls which ports are reachable |
For a small test application, a modest VPS may be sufficient. Production requirements depend on the application, concurrent users, memory usage, CPU workload, storage, and database requirements.
If you are planning a new deployment, choose a VPS with enough resources for the operating system, Docker, your application, and any supporting services.
Step 1: Connect to Your Linux VPS
Open a terminal on your local computer and connect to your VPS using SSH:
ssh username@YOUR_SERVER_IP
Replace username with your server account and YOUR_SERVER_IP with your VPS’s public IP address.
After logging in, check the operating system:
cat /etc/os-release
Check the available disk space and memory:
df -h
free -h
Make sure you have enough free storage for Docker images, container writable layers, application data, and logs.
Before making system changes, confirm that you have a backup or recovery method appropriate for your server.
Step 2: Install Docker Engine
For a production-oriented installation, use Docker’s official package repository rather than an unverified installation script.
The following commands are for supported Ubuntu and Debian systems using APT. Follow the installation instructions for your specific distribution on Docker’s official documentation site.
Official guides:
- Ubuntu: https://docs.docker.com/engine/install/ubuntu/
- Debian: https://docs.docker.com/engine/install/debian/
Install Docker on Ubuntu
First, update the package index and install the required tools:
sudo apt update
sudo apt install ca-certificates curl
Create the directory for APT repository keys:
sudo install -m 0755 -d /etc/apt/keyrings
Download Docker’s official repository signing key:
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
Make the key readable by APT:
sudo chmod a+r /etc/apt/keyrings/docker.asc
Add the Docker repository:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
Update the package index again:
sudo apt update
Install Docker Engine, the command-line tools, container runtime, Buildx, and the Compose plugin:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
If you use Debian, follow the Debian-specific repository instructions instead of copying the Ubuntu repository URL.
Start and verify Docker
Check the Docker service:
sudo systemctl status docker
If it is not running, start it:
sudo systemctl start docker
Enable automatic startup at boot:
sudo systemctl enable docker
Verify the installation:
sudo docker run hello-world
Docker should download the test image, run it, and display a confirmation message.
You can also check the installed versions:
sudo docker --version
sudo docker compose version
Docker Compose is available as a plugin in this installation, so you can use docker compose with a space rather than the older standalone docker-compose command.
Step 3: Create a Sample Web Application
To demonstrate deployment without depending on a particular programming framework, this example uses Nginx to serve a simple HTML page.
Create a directory for the application:
mkdir -p ~/docker-web-app/site
cd ~/docker-web-app
Create the HTML file:
cat > site/index.html <<'EOF'
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Docker VPS Deployment</title>
</head>
<body>
<h1>Docker application is running!</h1>
<p>This web page is served by Nginx inside a Docker container.</p>
</body>
</html>
EOF
This creates a small static website that you can use to verify that Docker networking and port publishing work correctly.
For a real Node.js, Python, PHP, or other application, you would use an appropriate application image or build one from a Dockerfile. The deployment concepts remain similar, but the container’s command, dependencies, storage, and environment variables will differ.
Step 4: Create a Docker Compose File
Docker Compose lets you describe the container configuration in a file instead of repeatedly typing a long command.
Inside ~/docker-web-app, create compose.yaml:
services:
web:
image: nginx:alpine
container_name: docker-web-app
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./site:/usr/share/nginx/html:ro
Here is what the configuration means:
services: Defines the containers managed by Compose.web: The name of this application service.image: Uses the Nginx Alpine image.container_name: Assigns a recognizable container name.restart: Restarts the container automatically after most unexpected stops or Docker daemon restarts.ports: Publishes host port 8080 and forwards traffic to port 80 inside the container.volumes: Makes the local website directory available to Nginx as read-only content.
The :ro suffix means the container cannot write to the mounted website directory through that mount.
For reproducible production deployments, consider pinning the image to a specific version or digest rather than relying on a moving tag.
Step 5: Start the Application
From the directory containing compose.yaml, run:
sudo docker compose up -d
The -d option runs the containers in the background.
Check the running containers:
sudo docker compose ps
You should see the web service running with port 8080 published on the VPS.
To inspect the application logs:
sudo docker compose logs --tail=100 web
If you change the HTML file, Nginx can serve the updated content from the mounted directory without rebuilding the image or recreating the container.
Step 6: Test the Application
Test the website directly from the VPS:
curl -I http://127.0.0.1:8080
A successful response should include an HTTP status such as 200 OK.
You can also inspect the returned HTML:
curl http://127.0.0.1:8080
If the request succeeds locally, open this address in a browser from your computer:
http://YOUR_SERVER_IP:8080
Replace YOUR_SERVER_IP with the actual public IPv4 address of your VPS.
If the application works locally but not from your computer, check the VPS firewall, provider firewall, network routing, and whether the published port is reachable externally.
Step 7: Configure Firewall Access Carefully
A public application needs a network path from the visitor to the server. For a temporary test, TCP port 8080 must be reachable from the client.
However, Docker port publishing has important firewall implications. On systems using UFW or firewalld, published Docker ports may bypass the firewall rules that administrators expect to control them. Review Docker’s firewall documentation before exposing a container port.
Official reference: https://docs.docker.com/engine/install/ubuntu/
For production websites, it is usually preferable to expose HTTP and HTTPS through a reverse proxy and keep internal application ports private.
If you use UFW, first inspect the existing rules:
sudo ufw status verbose
Do not enable or modify a firewall remotely without confirming that SSH access will remain available.
For a temporary demonstration, you can test from a trusted network or restrict access at the provider firewall. Avoid leaving unnecessary test ports publicly exposed.
Step 8: Put the Application Behind a Domain and HTTPS
A public IP address is useful for testing, but a production website generally benefits from a domain name and HTTPS.
A common architecture is:
- Point the domain’s DNS record to the VPS public IP address.
- Run the application container on an internal or loopback-bound port.
- Configure a reverse proxy to route requests to the application.
- Obtain and renew a TLS certificate.
- Expose only the required public ports.
For example, a reverse proxy can accept HTTPS requests for app.example.com and forward them to an application running on the VPS.
The exact setup depends on the reverse proxy and existing services. If Nginx or another web server is already listening on ports 80 and 443, avoid creating a port conflict. Plan the routing before changing the production configuration.
Important: Do not publish an application’s internal port to every network interface unless external access is genuinely required. For a simple application that is only accessed by a reverse proxy on the same VPS, a loopback binding such as 127.0.0.1:8080:80 can limit direct access to that port from outside the host.
For real production deployments, HTTPS configuration and certificate renewal should be tested before switching DNS traffic.
Step 9: Manage and Update the Container
Once your application is running, you will need a few basic Docker commands to manage it.
View running containers
sudo docker ps
To view all containers, including stopped ones:
sudo docker ps -a
View application logs
sudo docker compose logs -f web
Press Ctrl+C to stop following the logs. This does not stop the container.
Restart the application
sudo docker compose restart web
Stop the application
sudo docker compose down
This stops and removes the Compose-managed containers and network. It does not normally delete the application source files or named volumes, but you should always review the configuration before running cleanup commands.
Start the application again
sudo docker compose up -d
Update the image
For this example, the image is nginx:alpine. To pull the latest image for that tag and recreate the service:
sudo docker compose pull
sudo docker compose up -d
For production systems, review release notes and test updates before deployment. Pinning versions and keeping a rollback plan reduces the risk of an unexpected update breaking the application.
Step 10: Follow Docker Security Best Practices
Running Docker on a VPS requires attention to both the host and the containers.
Protect access to the Docker daemon
By default, access to the Docker daemon is highly privileged. Membership in the docker group can effectively grant root-level control over the host.
Do not add untrusted users to this group. Use sudo for administrative commands unless you have deliberately configured a safer alternative.
Avoid exposing sensitive ports
Only publish ports that must be reachable. Database services and internal APIs should normally stay on private Docker networks or be bound to a suitable restricted interface.
Use trusted images
Choose images from trusted publishers, review their documentation, and update them when security fixes become available. For production, pin versions or digests and test changes before rollout.
Store secrets securely
Avoid placing passwords, API keys, and database credentials directly in a public Compose file or source repository. Use appropriate secrets management and restrict access to configuration files.
Limit container privileges
Avoid using --privileged unless there is a specific, justified requirement. Run applications as non-root users where supported, minimize Linux capabilities, and mount only the directories the application needs.
Monitor and back up data
Check container logs, disk usage, and application health. Persistent data should be stored using an appropriate volume or bind mount and included in a tested backup strategy.
A Docker container is not a substitute for regular security updates, firewall configuration, access control, or backups.
Common Docker Deployment Problems
1. Port Already in Use
If Docker reports that a port is already allocated, another service or container may already be using it.
Check listening ports:
sudo ss -tulpn
You can change the host port in compose.yaml. For example, "8081:80" publishes the container’s port 80 on host port 8081.
2. Container Exits Immediately
Check the container status and logs:
sudo docker ps -a
sudo docker logs docker-web-app
A container may stop because of an invalid configuration, a missing dependency, an incorrect command, or an application error.
3. Cannot Access the Website Externally
First test locally:
curl -I http://127.0.0.1:8080
If that works, inspect the published port, firewall rules, provider network firewall, and external routing. Also verify that the application is bound to an interface reachable from the intended client.
4. Permission Denied When Running Docker
If you see a permission error connecting to the Docker socket, use sudo for the administrative command:
sudo docker ps
Avoid changing the Docker socket permissions to make it world-writable. That can expose privileged control of the host.
5. The Container Keeps Restarting
Inspect its logs and restart count:
sudo docker compose logs --tail=100 web
sudo docker inspect docker-web-app
A restart policy can restart a failed container, but it cannot fix the underlying application error. Find and resolve the root cause before treating the service as healthy.
Is a VPS Suitable for Docker Applications?
A VPS can be a practical environment for running Docker applications when you need control over the operating system, container runtime, network configuration, and application dependencies.
Before choosing a VPS, consider:
- CPU: Match the number of cores and processing capacity to your workload.
- RAM: Allow enough memory for the operating system, containers, and supporting services.
- Storage: Account for images, application files, logs, databases, and persistent volumes.
- Network: Confirm the required ports, bandwidth, and connectivity.
- Operating system: Use a distribution supported by the Docker installation method.
- Administration: Make sure you have the permissions required to install and maintain Docker.
For a simple test website, resource needs may be low. Applications with large databases, multiple services, or significant traffic need more careful sizing.
If you are selecting infrastructure for a new deployment, explore the available Linux VPS options from ServerNext and choose a configuration based on your application’s actual resource requirements.
Deploying a Docker application on a Linux VPS gives you a repeatable way to run web services and manage application dependencies. With Docker Engine, Docker Compose, and a suitable network configuration, you can start an application, inspect logs, restart services, and update containers through a consistent workflow.
For production use, go beyond simply getting the container to run. Protect the Docker daemon, restrict unnecessary ports, configure HTTPS, manage secrets carefully, monitor resource usage, and test backups and upgrades.
A properly maintained VPS with a well-configured Docker environment can provide a flexible foundation for websites, APIs, development tools, and self-hosted applications.
