Skip to content

How to Use uv init to Create a New Python Project

Setting up a traditional Python project takes minutes. Creating one with uv init takes seconds. That’s not an exaggeration - it’s the reality of using Astral’s Rust-based Python package manager.

I’ve been using uv for a while now, and uv init has completely changed how I start new Python projects. No more manually creating pyproject.toml, no more hunting for cookiecutter templates, no more waiting for Poetry to initialize. Just instant project scaffolding with proper structure.

Let me show you exactly how it works.

What is uv init?

uv init is a command that initializes a new Python project with a proper structure. It’s part of uv - the blazingly fast Python package manager from the creators of Ruff.

Here’s why I use it:

  • Creates pyproject.toml automatically with sensible defaults
  • Supports both application and library templates
  • Optional: README.md, .python-version file, Git initialization
  • 10-100x faster than traditional tools like Poetry or pip-tools

There are two modes: default (creates a full project structure) and bare (minimal - only pyproject.toml). I’ll cover both.

Prerequisites

First, install uv if you haven’t already:

Terminal window
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell)
irm https://astral.sh/uv/install.ps1 | iex
# Or via pip
pip install uv

Verify the installation:

Terminal window
uv --version

You should see something like uv 0.5.x - the version doesn’t matter much, but make sure it’s recent.

Quick Start - Create Your First Project

Here’s the simplest way to create a new project:

Terminal window
uv init hello-world
cd hello-world

That’s it. Let me show you what gets created:

hello-world/
├── .python-version
├── README.md
├── main.py
└── pyproject.toml

Now run it:

Terminal window
uv run main.py

You’ll see the output - a simple “Hello from uv!” message. But here’s the thing: uv run doesn’t just execute the script. It creates a virtual environment, installs dependencies, and runs your code. All in one command.

Project Types: Application vs Library

This is important. uv init creates different structures depending on whether you’re building an application or a library.

Application Project (Default)

Use this for web servers, scripts, CLIs - anything that runs rather than gets imported:

Terminal window
uv init my-app

Structure:

my-app/
├── .python-version
├── README.md
├── main.py
└── pyproject.toml

The main.py is your entry point. Simple.

Library Project (—lib flag)

Use this when you’re building something meant to be distributed via PyPI:

Terminal window
uv init my-lib --lib

Structure:

my-lib/
├── .python-version
├── README.md
├── pyproject.toml
└── src
└── my_lib
├── __init__.py
└── py.typed

Notice the src/ layout. This is the modern Python convention - it isolates your library code from project root imports, preventing accidental test contamination and making your package work correctly when installed.

The py.typed file? That’s how you tell type checkers (and PyPI) that your library includes type annotations.

Why does this matter? If you ever plan to publish to PyPI or want proper import isolation, use --lib. For scripts and quick experiments, the default app template is fine.

Customizing Project Creation

Sometimes you want less. Sometimes you want more. uv init has flags for both.

Bare Project (Minimal)

For CI/CD automation or when you want complete control:

Terminal window
uv init my-project --bare

Creates only pyproject.toml - nothing else:

my-project/
└── pyproject.toml

With Additional Options

Here are the flags I use most:

Terminal window
# With description
uv init my-project --description "My awesome project"
# Use author from Git config
uv init my-project --author-from git
# Initialize Git repo
uv init my-project --vcs git
# Pin Python version to .python-version
uv init my-project --python-pin

Combine them for maximum efficiency:

Terminal window
uv init example --bare --description "Hello world" --author-from git --vcs git --python-pin

Understanding pyproject.toml

Here’s what uv init creates:

[project]
name = "example"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []

Clean. Minimal. Ready to extend.

The requires-python field comes from your current Python version. If you want a specific version, use --python-pin or manually edit after creation.

Running Your Project with uv run

Now that you have a project, how do you run it?

Basic Execution

Terminal window
uv run main.py

This handles everything: creates/updates the virtual environment, resolves dependencies, runs your script.

Running with Temporary Dependencies

Need requests just for this one run? No need to add it to your project:

Terminal window
uv run --with requests main.py

The dependency installs temporarily, runs your script, and leaves your pyproject.toml untouched.

Running with Specific Python Version

Terminal window
uv run --python 3.10 main.py

Useful for testing compatibility or when you need a specific interpreter.

Running Arbitrary Commands

Terminal window
uv run -- flask run -p 3000

uv run isn’t limited to Python files. Any command in your project context works.

Adding Dependencies

When you’re ready to actually add dependencies to your project:

Terminal window
# Add a runtime dependency
uv add requests
# Add a dev dependency
uv add --dev pytest
# Add multiple at once
uv add requests httpx beautifulsoup4

This modifies your pyproject.toml and creates a lock file. Your dependencies are now reproducible across machines.

Best Practices and Tips

After using uv init extensively, here’s what I’ve learned:

  1. Use --bare for CI/CD - Less noise, more control. Your pipeline knows what it needs.

  2. Always use --lib for libraries - The src layout prevents import headaches. Trust me.

  3. Commit .python-version - It ensures reproducible builds across machines.

  4. Use uv lock - Commit the lock file. It locks your dependency versions.

  5. Use uv sync - On other machines, run uv sync instead of uv add to install exactly what’s locked.

Conclusion

uv init replaces a handful of tools I used to juggle: Poetry’s project creation, cookiecutter templates, manual pyproject.toml editing. Now it’s one command, sub-second execution.

Key takeaways:

  • uv init creates properly structured Python projects in seconds
  • Default mode = applications, --lib = libraries with proper src layout
  • Flags like --bare, --vcs git, and --python-pin customize what you get
  • uv run handles execution with automatic virtual environment management

Try it now:

Terminal window
uv init your-project-name
cd your-project-name
uv run main.py

Your turn.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments