K3s Kubernetes Setup: Lightweight Container Orchestration
Single and multi-node Kubernetes cluster setup with K3s, kubectl usage, creating deployments and services, application management with Helm. Kubernetes guide for VPS environments.
Docker security best practices: non-root user, read-only filesystem, seccomp profiles, image scanning, Docker socket security, network isolation, and secrets management.
Docker containers can harbor many security vulnerabilities in their default configuration. Containers running as root, over-privileged containers, and insecure images pose serious risks. In this guide, we'll cover Docker security layer by layer.
The most fundamental security measure: running containers as a non-root user.
# Bad practice — running as root
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]
# Good practice — non-root user
FROM node:20
# Create user and group
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN npm ci --only=production
# Switch to non-root user
USER appuser
CMD ["node", "server.js"]
# Run with specific user
docker run -d --user 1000:1000 myapp:latest
# Run with current user
docker run -d --user $(id -u):$(id -g) myapp:latest
# Check user
docker exec CONTAINER_NAME whoami
docker exec CONTAINER_NAME id
# Run with read-only root filesystem
docker run -d \
--name myapp \
--read-only \
--tmpfs /tmp \
--tmpfs /var/run \
myapp:latest
# Make specific directories writable
docker run -d \
--name myapp \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
-v app-logs:/app/logs \
myapp:latest
# docker-compose.yml
services:
app:
image: myapp:latest
read_only: true
tmpfs:
- /tmp
- /var/run
volumes:
- app-logs:/app/logs
Linux capabilities provide granular control over root privileges.
# Drop all capabilities, add only what's needed
docker run -d \
--name myapp \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
myapp:latest
# NEVER use privileged mode (security risk)
# docker run --privileged myapp # BAD PRACTICE
# Check current capabilities
docker exec CONTAINER_NAME cat /proc/1/status | grep Cap
# docker-compose.yml
services:
app:
image: myapp:latest
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
- CHOWN
Seccomp restricts the system calls a container can make.
# Run with default seccomp profile (recommended)
docker run -d --security-opt seccomp=default myapp:latest
# Custom seccomp profile
docker run -d \
--security-opt seccomp=/path/to/seccomp-profile.json \
myapp:latest
# Disabling seccomp (security risk)
# docker run --security-opt seccomp=unconfined myapp # BAD PRACTICE
# Minimal image with multi-stage build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production image — minimal
FROM node:20-alpine
# Apply security updates
RUN apk update && apk upgrade && apk add --no-cache dumb-init
# Non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
USER appuser
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]
# Scan with Trivy (recommended)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:latest image myapp:latest
# Only critical and high severity
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:latest image \
--severity CRITICAL,HIGH \
myapp:latest
# Scan with Docker Scout
docker scout cves myapp:latest
docker scout recommendations myapp:latest
# Restrict access to Docker socket
# Only add necessary users to docker group
usermod -aG docker appuser
# Mount Docker socket read-only (for monitoring)
docker run -d \
--name monitoring \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
monitoring-app:latest
# NEVER mount Docker socket in production containers
# This gives full access to the host!
# Environment variable secret (insecure — visible via docker inspect)
docker run -e DB_PASSWORD=secret myapp # BAD PRACTICE
# Use Docker secrets (Swarm mode)
echo "StrongPass123!" | docker secret create db_password -
docker service create \
--name myapp \
--secret db_password \
myapp:latest
# Secret accessible at /run/secrets/db_password
# docker-compose.yml
services:
app:
image: myapp:latest
env_file:
- .env
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
# Restrict inter-container communication
docker network create \
--driver bridge \
--opt com.docker.network.bridge.enable_icc=false \
secure-network
# Only expose necessary ports
docker run -d \
--name myapp \
-p 127.0.0.1:8080:8080 \
myapp:latest
# 127.0.0.1 restricts access to localhost only
# CPU and memory limits
docker run -d \
--name myapp \
--memory 512m \
--memory-swap 512m \
--cpus 1.0 \
--pids-limit 100 \
myapp:latest
// /etc/docker/daemon.json
{
"icc": false,
"no-new-privileges": true,
"userns-remap": "default",
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Docker security requires a multi-layered approach. Start with non-root user, read-only filesystem, minimal capabilities, and image scanning. Never pass secrets as environment variables, don't mount the Docker socket unnecessarily, and protect against DoS attacks with resource limits. With these security measures on REXE servers, you can keep your container infrastructure secure.
The root user inside a container can gain root privileges on the host in the event of a container escape vulnerability. Additionally, a container running as root can accidentally modify critical system files. Define a non-root user in your Dockerfile with the USER directive and run with the --user parameter.
Trivy is the most popular open-source tool: docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest image IMAGE_NAME. Integrate it into your CI/CD pipeline for automatic scanning on every build. Docker Scout is also the official Docker tool, used with docker scout cves IMAGE_NAME.
Almost never. --privileged gives the container near-full access to the host and eliminates all security isolation. If truly necessary (e.g., Docker-in-Docker), only use it in development environments. In production, use --cap-add to add only the capabilities you need.
Three methods: 1) .env file (simple but must be added to .gitignore), 2) Docker secrets (requires Swarm mode, accessible under /run/secrets/), 3) External secret management tools like HashiCorp Vault or AWS Secrets Manager. Never embed secrets in Dockerfiles or images.
no-new-privileges prevents processes inside the container from gaining new privileges through setuid/setgid binaries. This is an important protection layer against privilege escalation attacks. You can enable it by default for all containers by adding it to /etc/docker/daemon.json.
Single and multi-node Kubernetes cluster setup with K3s, kubectl usage, creating deployments and services, application management with Helm. Kubernetes guide for VPS environments.
Docker volume types (named, bind mount, tmpfs), volume management, Docker network modes (bridge, host, overlay), creating custom networks and container communication. Comprehensive guide.
Docker log drivers, docker logs command, Kubernetes pod logs, centralized log collection (Loki, ELK Stack), log rotation, and production log management best practices.