惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
量子位
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
B
Blog
U
Unit 42
C
Check Point Blog
I
InfoQ
aimingoo的专栏
aimingoo的专栏
雷峰网
雷峰网
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 【当耐特】
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
H
Help Net Security
MongoDB | Blog
MongoDB | Blog
博客园 - Franky
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
Microsoft Azure Blog
Microsoft Azure Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
云风的 BLOG
云风的 BLOG
宝玉的分享
宝玉的分享
爱范儿
爱范儿

Lobsters

CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Package managers that package package managers Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? CSS vs. JavaScript • Josh W. Comeau Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Back to the Building Blocks’ Building Blocks Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data
pyinfra — agentless infrastructure automation, in plain P...
pyinfra · 2026-06-15 · via Lobsters

automate
thousands of
servers

pyinfra is a python-native, agentless automation tool that runs commands over ssh — concurrently, idempotently, and 6× faster than ansible.

$ uv tool install pyinfra

[mit license] · [python 3.10+] · [no agents] · [zero config]

 1
 2 
 3from pyinfra.operations import apt, files, systemd
 4 
 5apt.packages(
 6    packages=["nginx", "certbot"],
 7    update=True,
 8)
 9 
10files.template(
11    src="templates/nginx.conf.j2",
12    dest="/etc/nginx/sites-enabled/api",
13)
14 
15systemd.service("nginx", reloaded=True)
 1
 2 
 3web = [
 4    ("web-01.prod", {"role": "edge"}),
 5    ("web-02.prod", {"role": "edge"}),
 6    *[(f"web-{i:02}.prod", {}) for i in range(3, 24)],
 7]
 8 
 9db = [
10    ("db-01.prod", {"role": "primary"}),
11    ("db-02.prod", {"role": "replica"}),
12]
13 
14
15
$ pyinfra inventory.py deploy.py --limit web
 
--> Loading inventory…
    Hosts: web-01..web-23
 
--> Gathering facts (concurrent)…
    23 hosts · 0.6s
 
--> Running deploy.py…
    ✓ web-01.prod   3 ops   changed=2   0.42s
    ✓ web-02.prod   3 ops   changed=2   0.39s
    ⟳ web-03.prod   running…  apt.packages
 
--> Summary
    successful: 23   changed: 18   failed: 0   total: 2.1s

NORMAL deploy.py python 23 hosts ready · --dry · 17:42

// streaming output

See what changes,
before it changes.

Run with --dry for a per-host diff of every operation pyinfra would perform. Run for real and watch results stream back in parallel.

bash · zsh · ~/ops live

$ pyinfra inventory.py deploy.py --limit web
 

--> Loading inventory…

Hosts: web-01..web-24, db-01..db-04  

--> Gathering facts (concurrent)…

24 hosts · 0.6s  

--> Running deploy.py…

✓ web-01.prod 3 ops changed=2 0.42s ✓ web-02.prod 3 ops changed=2 0.39s ✓ web-03.prod 3 ops changed=0 0.18s ✓ web-04.prod 3 ops changed=2 0.44s ⟳ web-05.prod running… apt.packages … 19 more  

--> Summary

successful: 24 changed: 18 no-change: 6 failed: 0 total: 2.1s

// features

Why pyinfra in six points.

def pure():

Just Python

No yaml. No jinja-in-yaml. Real control flow. Your editor already understands it.

def fast():

Concurrent ssh

6× faster than ansible on identical workloads. Built on gevent + SSH.

def safe():

Diff before apply

Run --dry to preview every change. Operations are idempotent — re-runs are no-ops.

def small():

0 agents

Only requirement on hosts: a shell and ssh. No daemons. No state files. No control plane.

def big():

Scale-ready

Works on 1 host or 10,000. Parallel execution, realtime streaming output.

def open():

Hackable

Custom operation in 10 lines. Connect to anything that speaks a shell — docker, lxc, k8s.

// vs ansible

$ diff ansible/ pyinfra/

--- ansible/playbook.yml 16 lines

- hosts: web
  tasks:
    - name: install nginx
      apt:
        name: nginx
        update_cache: yes
    - name: render config
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/sites-enabled/api
      notify: reload nginx
  handlers:
    - name: reload nginx
      service:
        name: nginx
        state: reloaded

+++ pyinfra/deploy.py 8 lines

from pyinfra.operations import apt, files, systemd

apt.packages(["nginx"], update=True)

cfg = files.template(
    src="nginx.conf.j2",
    dest="/etc/nginx/sites-enabled/api",
)
if cfg.will_change:
    systemd.service("nginx", reloaded=True)

// manifesto

#01

Code > config

A loop is just a loop. Stop encoding control flow into yaml.

#02

Show, then do

Diff first. Apply second. Surprise nobody.

#03

Stay out of the way

No agent. No state file. No control plane. SSH and go.

#04

Read like english

Operations are nouns and verbs. apt.packages. files.template. systemd.service.

// 180+ contributors

Thank you to hundreds open source contributors from companies and institutes all over...

SAP

EPAM Systems

Lawrence Livermore

Utrecht University

Odoo

Rochester Inst. of Tech.

Linköping University

Paul Scherrer Institute

Iress

NPR

Fox-IT

Prezi

Sensorfact

EDITED

Cynerio

# ready when you are

$ uv tool install pyinfra_

Read the 5-minute quickstart. Deploy your first host today. Replace your ansible repo next quarter.