Docker
CLI
docker build -t <tagname>
docker run -d <tagname>
docker ps --all
docker pull <docker>
docker container stop <id>
docker container rm <id>
docker images
docker image rm <id>
docker logs <container id>
docker exec -it <container id> cat server.js
docker cp . <container id>:/usr/src/app
docker run -p 8001:8001 -d --name <container> -e TZ=Asia/Kuala_Lumpur <image>
docker update --restart unless-stopped redis
systemctl start docker
docker stats --no-stream
docker inspect --format='{{.LogPath}}' container_id
docker compose up --no-start
docker compose run --rm <container> <command>Useful parameters
Change the time zone:
-e TZ=Asia/Kuala_Lumpur
Create a network
sudo docker network create --driver=bridge --subnet=172.18.0.0/16 --gateway=172.18.0.1 app-networkMariaDB using Docker
Create a database:
docker run -p 127.0.0.1:3306:3306 -v /root/database:/var/lib/mysql --name $dbname -e MYSQL_ROOT_PASSWORD=$1 -d mariadb
podman run -p 3306:3306 -v $dbpath:/var/lib/mysql:Z --name mariadb -e MYSQL_ROOT_PASSWORD=$1 -d mariadb
Z = private
z = shared
Start the database:
docker run -p 10.1.130.10:3306:3306 -v /root/database:/var/lib/mysql --name $dbname -d mariadb
Connect to the database:
docker run --rm -it --net=host mariadb mysql -h 10.1.130.10 -u root -p
If systemd keeps clearing the Podman cache file, use the command below:
loginctl enable-linger $userId
$userId is the user running Docker or Podman.
version: '3.3'
services:
database:
image: mariadb
container_name: mariadb
logging:
options:
max-size: "10m"
max-file: "3"
# join same network if available, else create bridges
# network_mode: bridge
# either one
networks:
app-network:
ipv4_address: 172.18.88.10
ports:
- "3306:3306"
environment:
TZ: Asia/Kuala_Lumpur
MARIADB_ROOT_PASSWORD: <mariadb-root-password>
volumes:
- /data/database:/var/lib/mysql
# command: npm run start
# entrypoint: npm run start
restart: unless-stopped
# Join external network
networks:
app-network:
name: app-network
external: trueHttpd using Podman
Create a Dockerfile:
echo "FROM httpd:2.4-alpine" > Dockerfile
Create the container:
sudo podman run -dit --name httpd -p 80:80 -p 443:443 httpd
sudo podman cp httpd:/usr/local/apache2/conf/ ./
Add the certificate, private key, and CA certificate to the ./conf folder.
Create import.sh with the following command:
sudo podman cp $1 httpd:/usr/local/apache2/$1For the httpd configuration, refer to Web Server.
Nginx using Docker
First-time setup
sudo docker run --name nginx -p 80:80 -p 443:443 -v -d nginx:alpine
sudo docker cp nginx:usr/share/nginx/html ./html
sudo docker cp nginx:etc/nginx/nginx.conf ./nginx.conf
sudo docker cp nginx:etc/nginx/conf.d ./conf.dRun the container
sudo docker run --name nginx -v /home/ubuntu/httpd/www:/usr/share/nginx/html -p 80:80 -p 443:443 -v /home/ubuntu/nginx/nginx.conf:/etc/nginx/nginx.conf -v /home/ubuntu/nginx/conf.d:/etc/nginx/conf.d -d nginx
sudo docker run --name nginx -v ./html:/usr/share/nginx/html -p 80:80 -p 443:443 -v ./nginx.conf:/etc/nginx/nginx.conf -v ./conf.d:/etc/nginx/conf.d -d nginxversion: '3.3'
services:
webserver:
# either from context or build from Dockerfile
image: nginx:alpine
# build: .
container_name: nginx
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
networks:
app-network:
ipv4_address: 172.18.88.2
environment:
TZ: Asia/Kuala_Lumpur
volumes:
- ./html:/usr/share/nginx/html
- ./nginx.conf:/etc/nginx/nginx.conf
- ./conf.d:/etc/nginx/conf.d
networks:
app-network:
driver: bridge
ipam:
config:
- subnet: 172.18.88.0/16
gateway: 172.18.88.1Let's Encrypt
Reference: Docker Community Forums
# install certbot
sudo docker run -it --rm -p 80:80 -p 443:443 --name certbot \
-v "/local-path/etc:/etc/letsencrypt" \
-v "/local-path/lib:/var/lib/letsencrypt" \
certbot/certbot certonly -v
sudo docker run -it --rm --name certbot \
-v "/local-path/etc:/etc/letsencrypt" \
-v "/local-path/lib:/var/lib/letsencrypt" \
-v "/local-path/.cloudflare:/etc/letsencrypt/cloudflare" \
certbot/dns-cloudflare renew \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare/config.iniconfig.ini
# Cloudflare API token used by Certbot
dns_cloudflare_api_token = <cloudflare-api-token>Alpine + Nginx + Certbot
FROM alpine:latest
RUN apk add --no-cache nginx certbot certbot-nginx
RUN mkdir /etc/letsencrypt
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]Then run:
docker build ./ -t nginx-certbotNode
Dockerfile
This uses the node user from the official image.
FROM node:22-alpine
WORKDIR /usr/src/app
RUN mkdir /usr/src/app/node_modules
RUN chown -R node:node /usr/src/appDocker Compose to set up a Node.js environment (docker-compose.yaml):
services:
node:
image: node:18-alpine
# Use Dockerfile to build
# build: .
container_name: node18
logging:
options:
max-size: "10m"
max-file: "3"
# join same network if available, else create bridges
network_mode: bridge
# either one
# networks:
# bridge:
# ipv4_address: 172.17.0.100
# user: "${UID}:${GID}"
user: "node"
working_dir: /usr/src/app
ports:
- "8088:8088"
environment:
TZ: Asia/Kuala_Lumpur
volumes:
# include all the file in /usr/src/app
- ./:/usr/src/app
# exclude node_modules
- /usr/src/app/node_modules
# - ./package.json:/usr/src/app/package.json
# - ./index.js:/usr/src/app/index.js
# - ./.env:/usr/src/app/.env
# - ./src:/usr/src/app/src
command: npm run start
# entrypoint: npm run start
# restart: unless-stopped
# Join external network
# networks:
# default:
# name: nginx-proxy_default
# external: trueIf Python is required, run:
apk add --no-cache python3 py3-pip make g++
Create networks
docker network create \
--driver=bridge \
--subnet=172.18.88.0/16 \
--ip-range=172.18.88.0/24 \
--gateway=172.18.88.1 \
app-networkUbuntu test
sudo docker run -ti --rm -p 8443:443 --name ubuntu-test ubuntu /bin/bash
apt install nginx certbot python3-certbot-nginxWordPress
version: '3.1'
services:
wordpress:
container_name: wordpress-server
image: wordpress
restart: always
ports:
- 8090:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: username
WORDPRESS_DB_PASSWORD: <wordpress-db-password>
WORDPRESS_DB_NAME: db_name
volumes:
- ./wordpress:/var/www/html
db:
container_name: wordpress-db
image: mysql:8.0
restart: always
environment:
MYSQL_DATABASE: db_name
MYSQL_USER: username
MYSQL_PASSWORD: <mysql-user-password>
MYSQL_RANDOM_ROOT_PASSWORD: <mysql-root-password>
volumes:
- ./db:/var/lib/mysql
volumes:
wordpress:
db:Production-ready recommendations
Use these defaults for long-running containers:
restart: unless-stopped
init: true
env_file:
- .env.production
logging:
options:
max-size: "10m"
max-file: "5"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10sGood hardening defaults for app containers:
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:trueFor Node services, prefer:
environment:
NODE_ENV: productionFor databases, prefer:
- persistent named volumes or host volumes on durable disks
- private network exposure instead of publishing ports publicly
- automated backups outside the container