TidyURLs
Go to Tools
BLOG
The TidyURLsJournal

Practical guides, tutorials, and insights about URLs, SEO, web development, and online marketing. Learn, troubleshoot, and get the most of the web.

Back to the Journal
TidyURLs Journal

How to Run an Application with Docker Without Installing Its Dependencies

Practical insights, clear explanations, and useful ideas for understanding the web.

October 25, 20267 min read5 topics
How to Run an Application with Docker Without Installing Its Dependencies
Explore the details
#Docker#DevOps#containers#Python#installation
October 25, 20267 min read

Docker lets you package an application and its dependencies into an image, then run that image in a container. This is useful when you want to try a project without installing its language runtime and libraries directly on your computer. In this guide, you’ll build and run a small Python web app, publish its port, pass an environment variable, check its logs, and clean up the container. You’ll also learn what Docker does not isolate or preserve automatically.

Key takeaways

  • A Dockerfile describes how to build an image; a container is a running instance of an image.
  • Dependencies installed during the image build are available inside the container, not automatically on your host.
  • Use port publishing to make a container service reachable from your computer. EXPOSE alone does not publish a port.
  • A container’s writable layer is temporary. Use a volume or bind mount when data needs to persist.
  • Docker reduces setup work, but it does not make untrusted code or images safe by itself.

What Docker changes about application setup

Without Docker, you might install a language runtime, install project packages, configure environment variables, and resolve version conflicts on your computer. With Docker, the project’s Dockerfile specifies an environment for building an image. Docker installs the project’s dependencies into that image, and a container runs from it.

You still need Docker installed and running on the host. Docker also downloads the base image and dependencies during the build, so an internet connection may be needed. Once the image has been built, running it usually does not require installing the application’s language dependencies on the host.

An image is a template; a container is an instance created from that template. Containers share the host’s operating-system kernel, so they are isolated environments, not full virtual machines.

Build a small Python application image

Create a new directory and add these two files. The example uses Flask to make a small web app. Flask will be installed in the image during the build, not on your computer’s Python environment.

app.py

import os

from flask import Flask

app = Flask(__name__)


@app.get("/")
def home():
    mode = os.environ.get("APP_MODE", "not set")
    return f"Hello from Docker! APP_MODE={mode}\n"


if __name__ == "__main__":
    port = int(os.environ.get("PORT", "8000"))
    app.run(host="0.0.0.0", port=port)

The app binds to 0.0.0.0 inside the container so that Docker’s port forwarding can reach it. Binding only to 127.0.0.1 inside the container would make the service reachable only from within that container.

requirements.txt

Flask==3.1.0

Pinning a dependency version makes this example more predictable than installing an unspecified latest version. For a real project, choose versions that are compatible with your application and update them deliberately.

Dockerfile

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

ENV PORT=8000
EXPOSE 8000

CMD ["python", "app.py"]

FROM selects the base image, WORKDIR sets the working directory, and COPY adds project files to the image. RUN executes during the build, while CMD specifies the default command when a container starts. EXPOSE documents the port the app uses; it does not make that port accessible from the host on its own.

Build and run the container

Run these commands from the directory containing the three files. Docker must be running.

Build an image and give it a name and tag:

docker build -t demo-web:1.0 .

The final . is the build context: the directory Docker can use to find files referenced by the Dockerfile. The first build may take longer because Docker needs to download the base image and install Flask. Later builds can reuse unchanged layers.

Start the app in the background and publish its container port on your computer:

docker run -d --name demo-web -p 127.0.0.1:8080:8000 -e APP_MODE=demo demo-web:1.0

The options mean:

  • -d runs the container in the background.
  • --name demo-web gives it a convenient name for later commands.
  • -p 127.0.0.1:8080:8000 maps host port 8080 to container port 8000 and limits the published host address to the local machine.
  • -e APP_MODE=demo sets an environment variable inside the container.
  • demo-web:1.0 is the image to run.

Open http://127.0.0.1:8080/ in a browser, or check the response with:

curl http://127.0.0.1:8080/

You should see Hello from Docker! APP_MODE=demo. The host port and container port can differ; if port 8080 is already in use, choose another host port, such as 127.0.0.1:8081:8000.

Configure the app and inspect its logs

Environment variables are a convenient way to supply non-secret configuration when a container starts. For example, the app uses APP_MODE and allows PORT to be overridden. If you change the container port, make the published container port match it. For example, running with -e PORT=9000 requires a mapping such as -p 127.0.0.1:8080:9000.

Read the app’s output with:

docker logs demo-web

Follow new log output as it arrives with:

docker logs -f demo-web

Press Ctrl-C to stop following the logs. That does not stop the background container. Stop it separately with docker stop demo-web.

Clean up containers and images

Stop and remove the container when you are finished:

docker stop demo-web
docker rm demo-web

To also remove the image, use:

docker rmi demo-web:1.0

You can instead start a disposable container with docker run --rm ...; Docker removes that container after its main process exits. This option is useful for short-lived commands, but it does not remove the image. A web server keeps running until it is stopped, so a detached server container will not be removed immediately.

Data persistence: what happens when a container is removed?

Files written to a container’s writable layer are associated with that container. If you remove the container, those changes are not preserved as a general-purpose data store. The image itself also does not capture changes made after the container starts.

For data that needs to outlast a container, mount a volume or a host directory at the path where the app writes data. For example, a named volume can be mounted like this:

docker run -d --name demo-web -p 127.0.0.1:8080:8000 -v app-data:/app/data demo-web:1.0

This only preserves files the application writes under /app/data; it does not automatically persist files written elsewhere. A bind mount can be useful during development when you want container processes to access host files, but changes can flow between the host and container. Choose mounts carefully and avoid exposing sensitive host directories unnecessarily.

Common mistakes and troubleshooting

  • The browser cannot connect: Confirm the container is running with docker ps, check the published port, and verify that the app binds to 0.0.0.0 inside the container.
  • The host port is already in use: Change the left-hand port in -p, for example 127.0.0.1:8081:8000, then visit port 8081.
  • A dependency is missing: Check that it appears in requirements.txt and that the build completed successfully. Rebuild the image after changing dependencies.
  • You changed a file but see old behavior: The running container uses the image it was created from. Build a new image, then recreate the container to use it.
  • You expect EXPOSE to publish the app: It does not. Use -p when starting the container to map a host port.
  • You expect files to survive removal: Use a volume or bind mount for the specific data directory that must persist.

Security and practical limitations

Docker is a packaging and isolation tool, not a guarantee that a program is safe. Only run images and application code you trust, and keep the Docker engine and base images maintained. For production, use a deliberately selected and maintained base image, and consider pinning it by digest when you need stronger control over exactly which image is used.

Avoid running the application as root when a non-root user is sufficient. Do not put passwords, API keys, or other secrets in a Dockerfile, image layers, or source control. Environment variables are useful for ordinary configuration, but they may be exposed through container inspection or operational tooling; use an appropriate secrets-management mechanism for sensitive values.

Publishing a port can make a service reachable beyond your computer depending on the address and network configuration. This example binds to 127.0.0.1 on the host to limit access to local connections. Review port bindings and firewall rules before exposing a service to a network. Also, the Flask development server is intended for development, not as a production web server; use a production-appropriate server and deployment setup for a real service.

Conclusion

Docker lets you build an application environment once and run it without installing the application’s language dependencies directly on your host. Start with a Dockerfile, build an image, publish the app’s port, and pass runtime configuration with environment variables. Before relying on a container, decide how its data will persist and apply appropriate security controls.