{
  "slug": "Getting-Started-with-Docker-files--A-Complete-Guide-d1d5b2d44d1b",
  "title": "Getting Started with Docker files: A Complete Guide",
  "subtitle": "Docker has revolutionized how developers build, ship, and run applications",
  "excerpt": "Docker has revolutionized how developers build, ship, and run applications",
  "date": "2025-12-01",
  "tags": [
    "Docker"
  ],
  "readingTime": "4 min",
  "url": "https://medium.com/@mobinshaterian/getting-started-with-docker-files-a-complete-guide-d1d5b2d44d1b",
  "hero": "https://cdn-images-1.medium.com/max/800/1*QoexvpDUN_--ehQG5QKvJg.png",
  "content": [
    {
      "type": "heading",
      "level": 2,
      "text": "Getting Started with Dockerfiles: A Complete Guide"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Introduction"
    },
    {
      "type": "paragraph",
      "html": "Docker has revolutionized how developers build, ship, and run applications. At the heart of Docker is the Dockerfile — a simple text file that contains instructions to build a Docker image. In this article, we’ll explore what Dockerfiles are, how to write them effectively, and best practices for creating production-ready containers."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Go-Simple Project"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Git Commit"
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*QoexvpDUN_--ehQG5QKvJg.png",
      "alt": "Getting Started with Docker files: A Complete Guide",
      "caption": "",
      "width": 1024,
      "height": 1024
    },
    {
      "type": "heading",
      "level": 2,
      "text": "What is a Dockerfile?"
    },
    {
      "type": "paragraph",
      "html": "A Dockerfile is a set of instructions that Docker uses to automatically build an image. Think of it as a recipe: it specifies the base operating system, installs dependencies, copies your application code, and defines how the container should run. Once you build an image from a Dockerfile, you can create and run containers based on that image."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Basic Dockerfile Structure"
    },
    {
      "type": "paragraph",
      "html": "A typical Dockerfile contains several key commands:"
    },
    {
      "type": "paragraph",
      "html": "<strong>FROM</strong> specifies the base image to build upon. This is usually an operating system like Alpine Linux, Ubuntu, or a language-specific image like Go or Python."
    },
    {
      "type": "paragraph",
      "html": "<strong>RUN</strong> executes commands during the build process, such as installing packages or dependencies."
    },
    {
      "type": "paragraph",
      "html": "<strong>COPY</strong> copies files from your local machine into the container."
    },
    {
      "type": "paragraph",
      "html": "<strong>WORKDIR</strong> sets the working directory inside the container where commands will run."
    },
    {
      "type": "paragraph",
      "html": "<strong>EXPOSE</strong> documents which ports the container will listen on."
    },
    {
      "type": "paragraph",
      "html": "<strong>CMD</strong> specifies the default command to run when the container starts."
    },
    {
      "type": "paragraph",
      "html": "Here’s a simple example:"
    },
    {
      "type": "code",
      "lang": "dockerfile",
      "code": "FROM golang:1.25-alpineWORKDIR /appCOPY . .RUN go mod downloadRUN\ngo build -o myapp .EXPOSE 8080CMD [\"./myapp\"]"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Choosing the Right Base Image"
    },
    {
      "type": "paragraph",
      "html": "The base image you choose significantly impacts your container’s size and capabilities. Alpine Linux is popular for its minimal footprint (around 5MB), making it ideal for production deployments. However, if you need more tools or libraries, you might prefer Ubuntu or Debian-based images, which are larger but more feature-rich."
    },
    {
      "type": "paragraph",
      "html": "For language-specific applications, official images like Go, Python, or Node are maintained and optimized for their respective languages. Using these saves you from manually installing language runtimes."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Multi-Stage Builds"
    },
    {
      "type": "paragraph",
      "html": "Multi-stage builds allow you to use multiple FROM statements in a single Dockerfile. This is powerful for reducing image size. In the first stage, you compile your application with all necessary build tools. In the second stage, you use a minimal runtime image and copy only the compiled binary."
    },
    {
      "type": "code",
      "lang": "dockerfile",
      "code": "FROM golang:1.21-alpine AS builderWORKDIR /appCOPY . .RUN\ngo build -o myapp .FROM alpine:latestCOPY --from=builder /app/myapp /root/CMD [\"./myapp\"]"
    },
    {
      "type": "paragraph",
      "html": "This approach produces much smaller final images since the build tools aren’t included in the runtime image."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Managing Dependencies"
    },
    {
      "type": "paragraph",
      "html": "When copying files into your container, it’s important to do so strategically. Copy your dependency files (like go.mod and go.sum) before copying the entire codebase. This leverages Docker’s layer caching: if your code changes but dependencies don’t, Docker can reuse the cached dependency layer rather than re-downloading everything."
    },
    {
      "type": "code",
      "lang": "dockerfile",
      "code": "COPY go.mod go.sum ./RUN go mod downloadCOPY . ."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Environment Variables and Configuration"
    },
    {
      "type": "paragraph",
      "html": "Applications often need configuration through environment variables. You can set default values in the Dockerfile using ENV, but for secrets or deployment-specific values, it’s better to pass them at runtime using the — env-file flag with docker run."
    },
    {
      "type": "paragraph",
      "html": "<code>docker run --env-file&nbsp;.env myapp:latest</code>"
    },
    {
      "type": "paragraph",
      "html": "This keeps sensitive information out of your image and allows different configurations for different environments."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Working with Volumes and Ports"
    },
    {
      "type": "paragraph",
      "html": "EXPOSE documents which ports your application uses, though it doesn’t actually publish the port. When running the container, you map these ports using the -p flag:"
    },
    {
      "type": "paragraph",
      "html": "<code>docker run -p 8080:8080 myapp:latest</code>"
    },
    {
      "type": "paragraph",
      "html": "The -t flag tags your image with a name and version. The dot at the end specifies the build context (current directory)."
    },
    {
      "type": "paragraph",
      "html": "To run a container from the built image:"
    },
    {
      "type": "paragraph",
      "html": "<code>docker run -p 8080:8080 myapp:latest</code>"
    },
    {
      "type": "paragraph",
      "html": "Volumes allow containers to persist data or access files from your host machine. You can define volumes in the Dockerfile or when running the container."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Building and Running"
    },
    {
      "type": "paragraph",
      "html": "To build an image from a Dockerfile:<br><code>docker build -t myapp:latest&nbsp;.<br></code>The -t flag tags your image with a name and version. The dot at the end specifies the build context (current directory)."
    },
    {
      "type": "paragraph",
      "html": "To run a container from the built image:"
    },
    {
      "type": "paragraph",
      "html": "<code>docker run -p 8080:8080 myapp:latest<br></code>To run with an environment file:<br><code>docker run -p 8080:8080 --env-file&nbsp;.env myapp:latest<br></code>Best Practices"
    },
    {
      "type": "paragraph",
      "html": "<strong>Keep images small</strong>: Use minimal base images, remove unnecessary files, and leverage multi-stage builds. Smaller images deploy faster and have a smaller attack surface."
    },
    {
      "type": "paragraph",
      "html": "<strong>Use&nbsp;.dockerignore</strong>: Just like&nbsp;.gitignore, a&nbsp;.dockerignore file prevents unnecessary files from being copied into your image, reducing build time and image size."
    },
    {
      "type": "paragraph",
      "html": "<strong>Order commands strategically</strong>: Place commands that change frequently at the end of the Dockerfile to maximize layer caching. This speeds up rebuilds."
    },
    {
      "type": "paragraph",
      "html": "<strong>Don’t run as root</strong>: For security, create a non-root user to run your application. This limits damage if the container is compromised."
    },
    {
      "type": "paragraph",
      "html": "<strong>Health checks</strong>: Define health checks so Docker knows if your application is running correctly and can restart unhealthy containers.<br><code>HEALTHCHECK --interval=30s --timeout=10s CMD curl -f http://localhost:8080/health || exit 1<br></code><strong>Use specific base image versions</strong>: Avoid using latest tags, as they can change unexpectedly. Specify explicit versions for reproducibility."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Conclusion"
    },
    {
      "type": "paragraph",
      "html": "Dockerfiles are fundamental to containerization. By understanding their structure and following best practices, you can create efficient, secure, and maintainable container images. Start simple, experiment with different configurations, and gradually incorporate advanced techniques like multi-stage builds and health checks. With practice, writing Dockerfiles becomes second nature, and you’ll be able to containerize any application with confidence."
    }
  ]
}
