Hello everyone! So this article will be on how you can create your own Docker registry hub and push your own Docker images to it. So before we start, here are the prerequisites. They're obvious, but let's make sure.
12 Must Use Docker Commands (Docker - Zero to Hero)
Posted on April 14th of this year at 9:11 A.M.707 views24 min read
We all know Docker is a powerful tool for developers and it allows us to spin up systems to run our applications on without impacting the host machine. This allows us to have our applications in a containerized bubble and allows us to monitor such a system with various built-in Docker commands. The $ in these examples indicates a Linux terminal. There's nothing to worry about. Below are the top 12 must use Docker commands that I recommend.
docker start and docker stop
The docker start and docker stop commands are pretty important in a case where your container may be broken. You may want to stop your container if the container continues to crash. In any case it is recommended to always check the logs of your containers to debug the reason why the containers are crashing. The docker start command obviously should be used once you have figured out why your containers were crashing and you have put a fix in it. Below is an example on how to use the docker start and docker stop commands.
# Stop example container
$ docker stop example-container
# Start example container
$ docker start example-container
In the example above, all we're really doing is just stopping our example-container and then starting our example-container. That's pretty much all for these 2 commands.
docker rm
The docker rm command can be used to delete a container. That's pretty much all this command is used for. If you no longer require the container, you can simply delete your container by running the docker rm command.
# Delete container
$ docker rm example-container
The above example is pretty straight forward. All we're doing is deleting our example container.
docker rename
The docker rename command can come in handy if you want to rename your containers. Sometimes we make mistakes and make typos when creating things. The docker rename command allows you to rename your container in case that ever happens.
# Rename your old container
$ docker rename example-mistake-container example-correct-container
The above example is pretty straight forward. All we're doing is renaming our container.
docker cp
The docker cp command is extremely useful if you want to copy files from an existing Docker container. You can't copy files from 1 container to another however you can actually copy files from 1 container onto the host machine and then from the host machine to another container. So the host machine has to act as the middle man for you to copy the files between containers. It's not hard to do, but it's just tedious. Below is an example on how to do such an operation.
# Create an example folder and then switch to that example folder
$ mkdir ~/example-container-files ; cd ~/example-container-files
# Copy from 1st container to host machine
$ docker cp example-one-container:/some/folder/name/myFiles .
# Copy from host machine to 2nd container
$ docker cp myFiles example-two-container:/some/folder/name
In the example above, we create a folder called example-container-files at the root of the current user's home directory. Then we switch to that directory and then copy down the folder called myFiles to the host machine. From there we copy the myFiles folder to the 2nd container's location. This can be any location you want, just as long as you know where you're copying those folders and files to. This can be done to an entire folder as we see in this example or it can be done to a standalone file. Both works the same way.
docker logs
The docker logs command is extremely useful for debugging why a container is crashing. Sometimes the container may not output logs, but it's still useful in case the container does output logs.
# Output current logs
$ docker logs example-container
In the example above, all we're doing is outputting the current logs that are coming through from our example-container.
Clearing logs
Now clearing container logs isn't so easy as just running a docker command. We have to use a little bit of Linux to actually clear container logs. Why do we want to clear container logs you ask? Well just like any kind of logs, the more logs you incur, the more time it takes for the logs to output to the screen and thus the more time it takes to debug why a container may be crashing. If you attempt to fix the issue with a crashing container and want to try to re-output the logs, it'll take time to comb through those logs. Then if that fix doesn't work then you'll have to put in a new fix and try to confirm your new fix worked by outputting the logs again. If you have a fresh set of logs, it's much easier to comb through rather than a gigantic wall of logs every time you want to confirm a fix has worked. Logs don't clear themselves.
$ echo "" > $(docker inspect --format='{{.LogPath}}' <container_name_or_id>)
You can however create a function in Linux to run the command above with a supplied parameter (the parameter being your container name) so that it's easier to use rather than typing all of that out. Below is an example for Linux. You'll have to store this in your .bashrc file for your current user.
docker-clear() {
# Clear specified container's logs
echo "" > $(docker inspect --format='{{.LogPath}}' $1)
}
To use this newly created function, all you would have to do is something like
$ docker-clear example-container
For folks using the Docker Desktop app, you can actually just clear the logs when you're inspecting the container. Here are the steps to doing so.
- Open up the Docker Desktop program
- Click on the Containers tab way on the top left corner side of the Docker Desktop program if you aren't already in the Containers section of the Docker Desktop program
- Click on the container name you want to clear the logs for
- Click on the Logs tab if you aren't already there
- On the right hand side there should be a trash can icon, hover over it and it should say Clear terminal
- Click on that and it should clear the logs
docker ps and docker ps -a
The docker ps and docker ps -a commands are useful for displaying currently running containers. The only difference between the command with and without the -a flag is that the one with the -a flag displays all containers whether that'd be running, stopped, or restarting containers. The one without the -a flag only displays currently running or restarting containers. Both will display created containers. These 2 commands don't require or use a container's name after the command. Below is how you would use the 2 commands.
# Display only running and restarting containers
$ docker ps
# Display running, restarting, and stopped containers
$ docker ps -a
docker inspect
The docker inspect command can actually be used on both containers and Docker networks. This is a really nifty command as this will let you know what the configurations are for that specific container or network. So things such as environment variables, attached IP addresses, attached ports, container names, network names, container statuses, lists of attached containers on a network, etc will all show up in this command.
# Creating a Docker network and inspecting that docker network
$ docker network create myNetwork
$ docker inspect myNetwork
# Inspecting a container
$ docker inspect example-container
Here is an example of a network output from the docker inspect command.
[
{
"Name": "myNetwork",
"Id": "bef8653a1da57632993069b6eb0aedf6eace1782a32414a50b96472ca991dfdf",
"Created": "2023-12-29T06:35:00.550543807Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855": {
"Name": "example-one-container",
"EndpointID": "7771818d76f16b6fd615f03dfc5d8ffceafbad86091ed181b1054b7ce16c4e34",
"MacAddress": "xx:xx:xx:xx:xx:xx",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
"421c76d77563afa1914846b010bd164f395bd34c2102e5e99e0cb9cf173c1d87": {
"Name": "example-two-container",
"EndpointID": "bae0bb8654d7ff85e30befe0a772f29d71c3dc9a925b52b4203e588760f9ffa8",
"MacAddress": "xx:xx:xx:xx:xx:xx",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
Here is an example of a container output from the docker inspect command.
[
{
"Id": "b1957e6254a0678e89362fbc8301378577891837cad4759ce793b012ee63cd81",
"Created": "2023-12-29T16:13:44.456187853Z",
"Path": "/docker-entrypoint.sh",
"Args": [
"nginx",
"-g",
"daemon off;"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 000000,
"ExitCode": 0,
"Error": "",
"StartedAt": "2024-04-12T13:03:22.7988656Z",
"FinishedAt": "2024-04-12T13:03:06.166360381Z"
},
"Image": "sha256:cb6e240703835c834a9faae406eb25035771f69525eb1c3ea497b2d3ae4f8037",
"ResolvConfPath": "/var/lib/docker/containers/49eecf6e703f5fe3abd5d2e257e232ec3c6e63cd7fa985ecd1cfda419470f81c/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/49eecf6e703f5fe3abd5d2e257e232ec3c6e63cd7fa985ecd1cfda419470f81c/hostname",
"HostsPath": "/var/lib/docker/containers/49eecf6e703f5fe3abd5d2e257e232ec3c6e63cd7fa985ecd1cfda419470f81c/hosts",
"LogPath": "/var/lib/docker/containers/49eecf6e703f5fe3abd5d2e257e232ec3c6e63cd7fa985ecd1cfda419470f81c/49eecf6e703f5fe3abd5d2e257e232ec3c6e63cd7fa985ecd1cfda419470f81c-json.log",
"Name": "/example-container",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "unconfined",
"ExecIDs": [
"3035a31b8d103b2a7679ea03726e927fed75a081acf865ef6a9e1f233f1ca0bc"
],
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "myNetwork",
"PortBindings": {
"443/tcp": [
{
"HostIp": "",
"HostPort": "4433"
}
],
"80/tcp": [
{
"HostIp": "",
"HostPort": "8080"
}
]
},
"RestartPolicy": {
"Name": "always",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"ConsoleSize": [
55,
183
],
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "private",
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": true,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": [
"label=disable"
],
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": [],
"BlkioDeviceWriteBps": [],
"BlkioDeviceReadIOps": [],
"BlkioDeviceWriteIOps": [],
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": null,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": null,
"ReadonlyPaths": null
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/5e09b9d58f344e5c56a8e0e73e99e9d65354e6c8d068228827fb64978f8a3b01-init/diff:/var/lib/docker/overlay2/48c6f0e115d83579b5519e47269fd42eb3121501bedfd3db82f666e3e2fbc473/diff:/var/lib/docker/overlay2/3a5e93c30e12f615f58dba29f9caf7857e2bc129ae21e8d51f4e481b5c7c6b1f/diff:/var/lib/docker/overlay2/a4ab606f28b8854d9c26845325563d18a5cc26d61a7796e6caac53a9bf8c58cb/diff:/var/lib/docker/overlay2/5411ef14b3f9504cfb3973efe73359b85bb6c5ce3a16e18b26d5944e13429fa3/diff:/var/lib/docker/overlay2/9ede0fa5aeecb5f1829d497fa55e8785e8f99ed630250a047f8239adf8d53427/diff:/var/lib/docker/overlay2/aa1066ac0c8ad8a4da8813b7b30f0689debe286d35fa576b8552ea172915f3c0/diff:/var/lib/docker/overlay2/acda82fec0fe3e0c0f6740064de9d374af4a0a1e15f470322d8c1e9e0b38d1aa/diff",
"MergedDir": "/var/lib/docker/overlay2/5e09b9d58f344e5c56a8e0e73e99e9d65354e6c8d068228827fb64978f8a3b01/merged",
"UpperDir": "/var/lib/docker/overlay2/5e09b9d58f344e5c56a8e0e73e99e9d65354e6c8d068228827fb64978f8a3b01/diff",
"WorkDir": "/var/lib/docker/overlay2/5e09b9d58f344e5c56a8e0e73e99e9d65354e6c8d068228827fb64978f8a3b01/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "example-container.localhost",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"443/tcp": {},
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.25.2",
"NJS_VERSION=0.8.0",
"PKG_RELEASE=1~bookworm"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
"Image": "nginx:1.25.2",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/docker-entrypoint.sh"
],
"MacAddress": "02:42:ac:12:00:17",
"OnBuild": null,
"Labels": {
"maintainer": "NGINX Docker Maintainers"
},
"StopSignal": "SIGQUIT"
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "85fa7c028464e214b6f0bdba546156131a3c331a0fdad71acaf3a666171a7c4d",
"SandboxKey": "/var/run/docker/netns/85fa7c028464",
"Ports": {
"443/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "4433"
}
],
"80/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "8080"
}
]
},
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"myNetwork": {
"IPAMConfig": {
"IPv4Address": "172.18.0.2"
},
"Links": null,
"Aliases": [],
"MacAddress": "xx:xx:xx:xx:xx:xx",
"NetworkID": "270a0f36fcdff52a035ef28ed5e66776c5ae6607a96e8530f20e71428517da31",
"EndpointID": "a63d38392cd1ea858580d4a900cafd0e65bfb85dfaee0a2f9fb20475888d95dd",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"DriverOpts": {},
"DNSNames": [
"example-container",
"b1957e6254a0",
"example-container.localhost"
]
}
}
}
}
]
docker images
The docker images command will show you all the images you've pulled down from the internet. These images can either be from the official Docker hub or from a private registry.
# List all locally pulled Docker images
$ docker images
This command is pretty straight forward and only is used to verify what images you currently have on the host machine.
docker pull
The docker pull command will actually pull directly from the official Docker hub first. If you want to also pull from a private registry hub, you'll actually want to add that into your Docker configurations. For folks using the Docker Desktop app, here are the steps.
- Open Docker Desktop program
- Click on the cog icon at the top right corner of the Docker Desktop program
- Click on the Docker Engine tab
- If you don't already have an insecure-registries line then add that to the JSON string in the Docker Engine section
"insecure-registries": [ "private-docker-hub-here", "private-docker-hub-here:5000" ]
- Apply the changes and restart the Docker Desktop program and or the Docker Engine
For my Linux users, you'll want to create a daemon.json file if you haven't already done so located here /etc/docker/daemon.json. Same insecure-registries code from above will need to be applied to this daemon.json file.
Below is the command to pull from either the official Docker hub or from your private registry hub if you've already applied the configuration settings from above. I don't know if there's an order in which Docker will pull the image from. As long as the image exists in either of those 2 locations; official Docker hub or private registry hub, then Docker will pull that image either way.
# Pull an image either from the official Docker hub or from a private registry
$ docker pull example-image:tag
docker push
The docker push command is basically just like the docker pull command however where ever you are trying to push your image to, you will need to have access and or an account on that particular hub you're trying to push your image to.
# Create the image tag first
# The local image needs to come first then the actual domain/IP address of either the official Docker hub or the private registry hub you want to push to
$ docker tag example-image:0.1 official-docker-hub-or-private-docker-hub-here/example-image:0.1
# Push the image to either the official Docker hub or to a private registry hub
$ docker push official-docker-hub-or-private-docker-hub-here/example-image:0.1
Why would you want to push an image to either the official Docker hub or to a private registry hub? Well you can basically create your own Docker image and have other people pull it down and create containers based off that. Basically "sharing" images if you will. The next Docker command will go into further details on this.
docker build
Saving the best for last there's the docker build command. This command allows you to build your own Docker image using a Dockerfile. This Dockerfile can be customized to your liking. You can also import from an existing Docker image and then do whatever you want within it. For instance, you can pull a base Ubuntu image and in your Dockerfile you can actually install any package you'd like. So you can turn a base Ubuntu image into say an image that contains Java if you really wanted to.
# Change directory to the folder that has the Dockerfile
$ cd ~/docker-image
# Build the image from the Dockerfile
$ docker build -t {image}:{tag} .
Here is an example of a Dockerfile using the above Java example.
# Import from base image out there on the Docker hub FROM ubuntu:latest # Change to the root user USER root # Run these commands # Update the system # Install these packages # software-properties-common (contains both add-apt-repository and apt-add-repository commands) # lsb-release (allows you to display the currently installed Ubuntu version) # apt-transport-https (allows you to download things via the HTTPS protocol rather than the HTTP protocol) # ca-certificates (contains all trusted authorized certificates from the certificate authorities) # wget (command that allows you to download things from the internet) # sudo (command that allows you to run things at the system level) # htop (command that allows you to monitor CPU and memory usage) # openssh-server (contains the SSH technology so you can remote into your container) # vim (command that allows you to edit files) # net-tools (contains the ifconfig command - this allows you to display the local IP address e.g. 172.8.0.36) # zip (command that allows you to compress files) # git (contains the Git technology so that you can clone down Git repositories off the internet) # inetutils-ping (contains the ping command so you can check for internet connectivity) # openjdk-21-jdk (contains the Java technology) # Update the system RUN apt-get -y update ; apt-get -y install software-properties-common lsb-release apt-transport-https ca-certificates wget sudo htop openssh-server vim net-tools zip git inetutils-ping openjdk-21-jdk ; apt-get -y update # Create the docker user ARG user=docker ARG group=docker ARG uid=1000 ARG gid=1000 RUN groupadd -g ${gid} ${group} RUN useradd -u ${uid} -g ${group} -s /bin/sh -m ${user} # Switch to the docker user USER ${uid}:${gid} # Set the entrypoint to spit out logs ENTRYPOINT ["tail", "-f", "/dev/null"]
Then we switch to where this Dockerfile is stored and we run the docker build command like so
# Change directory to the folder that has the Dockerfile
$ cd ~/java-image
# Build the image from the Dockerfile
$ docker build -t java-image:0.1 .
Once the build has completed, you can then create your container based off that image or you can actually push that image to either the official Docker hub or to a private registry hub.
With that being said, hopefully this article is very useful for any of my upcoming new developer friends or if you're already a developer yourself.