mirror of
https://github.com/kevinveenbirkenbach/create-linux-swapfile.git
synced 2026-10-25 23:42:19 +00:00
On a btrfs root the swapfile was written with dd from /dev/zero, so a swapfile the size of the RAM cost tens of GiB of writes and stalled every other writer on the filesystem. A caller that bounds the run, such as an async Ansible task, killed dd halfway, and the next run found the wrong size and started over. btrfs accepts a preallocated NOCOW swapfile and the swap directory already carries +C, so every filesystem now allocates with fallocate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
139 lines
5.1 KiB
Markdown
139 lines
5.1 KiB
Markdown
# SwapForge (swap-forge) 🔄
|
||
|
||
[](https://github.com/sponsors/kevinveenbirkenbach) [](https://www.patreon.com/c/kevinveenbirkenbach) [](https://buymeacoffee.com/kevinveenbirkenbach) [](https://s.veen.world/paypaldonate)
|
||
|
||
[](LICENSE) [](https://github.com/kevinveenbirkenbach/swap-forge/stargazers)
|
||
|
||
SwapForge is a small Python CLI for creating and managing Linux swapfiles. Whether you need to boost system performance or add swap space to your setup, SwapForge automates the process quickly and reliably — and it knows the difference between btrfs and everything else.
|
||
|
||
---
|
||
|
||
## 🧭 How it works
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["swap-forge SIZE"] --> B["parse SIZE into MiB"]
|
||
B --> B2{"parsable?"}
|
||
B2 -- no --> X["Invalid size format - exit 1"]
|
||
B2 -- yes --> C["findmnt -no FSTYPE /"]
|
||
C --> D{"root filesystem is btrfs?"}
|
||
D -- yes --> E["target: /var/swap/swapfile"]
|
||
D -- no --> F["target: /swapfile"]
|
||
E --> G["read the size of the existing swapfile"]
|
||
F --> G
|
||
G --> H{"same size already?"}
|
||
H -- yes --> Y["Skipping - exit 0"]
|
||
H -- no --> I{"a swapfile is already there?"}
|
||
I -- yes --> J["swapoff, delete it, drop its fstab line"]
|
||
I -- no --> K
|
||
J --> K{"root filesystem is btrfs?"}
|
||
K -- yes --> L["chattr +C, turn compression off"]
|
||
K -- no --> M["allocate with fallocate"]
|
||
L --> M
|
||
M --> N["chmod 600"]
|
||
N --> O["mkswap"]
|
||
O --> P["swapon"]
|
||
P --> Q["append the entry to /etc/fstab"]
|
||
```
|
||
|
||
The fstab entry is written **after** activation succeeds, so a failed run never leaves a half-written fstab behind.
|
||
|
||
---
|
||
|
||
## 🛠 Features
|
||
|
||
- **Automated Swapfile Creation:** Easily create a swapfile with a specified size.
|
||
- **Btrfs Aware:** Disables copy-on-write and compression on the swap directory before allocating; every filesystem allocates with `fallocate`.
|
||
- **FSTAB Integration:** Automatically updates `/etc/fstab` to ensure the swapfile is mounted at boot.
|
||
- **Safety Checks:** Skips creation when a swapfile of the correct size already exists, and recreates it when the size differs.
|
||
- **Simple CLI Interface:** Run the command with a single argument.
|
||
|
||
---
|
||
|
||
## 📥 Installation
|
||
|
||
```bash
|
||
pip install swap-forge
|
||
```
|
||
|
||
pip is the single supported installation path.
|
||
|
||
The package installs **two** identical commands: `swap-forge` (primary) and `swafo` (kept for older documentation and scripts).
|
||
|
||
---
|
||
|
||
## 🔧 Requirements
|
||
|
||
- **Python 3.10+** 🐍
|
||
- **root privileges** — the tool writes `/etc/fstab` and activates swap
|
||
- Always required on `PATH`: `findmnt`, `swapoff`, `fallocate`, `chmod`, `mkswap`, `swapon`
|
||
- On a **btrfs** root additionally: `chattr`, `btrfs`
|
||
|
||
If a required command is missing, the tool exits with code `127` and a one‑line error instead of a traceback.
|
||
|
||
---
|
||
|
||
## 🚀 Usage
|
||
|
||
Run SwapForge by specifying the desired swapfile size. For example, to create a 2G swapfile:
|
||
|
||
```bash
|
||
sudo swap-forge 2G
|
||
```
|
||
|
||
Accepted sizes are whole numbers with an optional unit: `2048`, `2048M`, `2048MB`, `64G`, `64GB`. Without a unit the value is read as MiB.
|
||
|
||
The swapfile location follows the root filesystem:
|
||
|
||
| Root filesystem | Swapfile |
|
||
| --- | --- |
|
||
| `btrfs` | `/var/swap/swapfile` |
|
||
| anything else | `/swapfile` |
|
||
|
||
SwapForge compares the existing swapfile against the requested size, skips when they match, and otherwise removes the old one — including its `/etc/fstab` line — before creating the new one.
|
||
|
||
### Exit codes
|
||
|
||
| Code | Meaning |
|
||
| --- | --- |
|
||
| `0` | Swapfile is in place, either created or already correct. |
|
||
| `1` | Invalid size argument, or one of the swap commands failed. |
|
||
| `2` | Invalid command line arguments. |
|
||
| `127` | A required command is not installed. |
|
||
|
||
---
|
||
|
||
## 🧪 Development
|
||
|
||
```bash
|
||
make lint # ruff check + ruff format --check
|
||
make format # apply ruff format
|
||
make test # unit + integration tests
|
||
make test-unit
|
||
make test-integration
|
||
make test-e2e # install the package in a container and exercise the CLI
|
||
```
|
||
|
||
Tests run against the working tree — the `Makefile` puts `src/` on `PYTHONPATH`, so no install is needed. Every command execution is mocked and `fstab` handling is exercised against a temporary file, so the suite never touches real swap.
|
||
|
||
---
|
||
|
||
## 🧑💻 Author
|
||
|
||
Developed by **Kevin Veen-Birkenbach**
|
||
|
||
- 📧 [kevin@veen.world](mailto:kevin@veen.world)
|
||
- 🌐 [https://www.veen.world](https://www.veen.world)
|
||
|
||
---
|
||
|
||
## 📜 License
|
||
|
||
This project is licensed under the **MIT License**.
|
||
|
||
---
|
||
|
||
## 🤝 Contributions
|
||
|
||
Contributions are welcome! Feel free to fork the repository, submit pull requests, or open issues if you have suggestions or encounter any problems. Let's make Linux swap management easier together! 😊
|