>_ZSH-NGINX
nginx vhost lifecycle, one tab away. A zsh plugin for the Debian/Ubuntu nginx layout ($NGINX_DIR/sites-available/ holds configs, $NGINX_DIR/sites-enabled/ is a symlink farm). Three autoloaded functions — en, dis, vhost — cover create / enable / disable; two aliases — ngt, ngr — cover the test / restart cycle; three filesystem-driven completions feed real on-disk site names into <TAB>.
Overview
zsh-nginx is a single source line that registers a small surface for managing nginx virtual hosts on the Debian-style split layout. Sourcing the plugin sets two environment defaults, probes for sudo, defines the ngt / ngr aliases, prepends src/ (completions) and appends autoload/ (functions) to fpath, and registers en / dis / vhost via autoload -Uz. Nothing else runs at source time — the function bodies load lazily on first call. Re-sourcing the plugin is idempotent: the env defaults use : ${VAR:=default} so existing values are never clobbered.
| en NAME | Enable a site — symlink it from sites-available into sites-enabled |
| dis NAME | Disable a site — remove its sites-enabled symlink (config untouched) |
| vhost … NAME | Generate a vhost config from a template, optionally enable it and write /etc/hosts |
ngt | Test the running nginx config (nginx -t) |
ngr | Restart the nginx service |
Install
# Zinit
zinit ice lucid nocompile
zinit load MenkeTechnologies/zsh-nginx
# Oh My Zsh
git clone https://github.com/MenkeTechnologies/zsh-nginx \
"$HOME/.oh-my-zsh/custom/plugins/zsh-nginx"
# then: plugins+=(zsh-nginx)
# Manual
git clone https://github.com/MenkeTechnologies/zsh-nginx
source zsh-nginx/zsh-nginx.plugin.zsh
Aliases
ngt | $sudo nginx -t — test the running config for syntax errors |
ngr | $sudo service nginx restart — restart the service |
Both aliases are prefixed with $sudo. The entrypoint probes which -p sudo at source time: if sudo is on PATH the prefix is sudo, otherwise it is the empty string and the command runs as the current user. On a systemd-only box where service nginx restart is not wired up, redefine the ngr alias to use systemctl restart nginx.
Functions
// en NAME
Enables a site by creating ln -s $NGINX_DIR/sites-available/NAME $NGINX_DIR/sites-enabled/NAME. It refuses to run without a name, errors if the config does not exist in sites-available, and reports if the site is already enabled. After the symlink is created it re-checks that the link materialised before printing success. Because it is a symlink (not a copy or move), disabling and re-enabling is lossless.
// dis NAME
Disables a site by rm -f $NGINX_DIR/sites-enabled/NAME — it removes only the symlink, never the underlying config in sites-available. It requires a name, errors if the site is not currently enabled, and re-checks that the symlink is gone before reporting success.
// vhost [options] NAME
Generates a vhost config from a template and (by default) enables it. The flow: parse flags with getopts, resolve the template, look up the owning user in /etc/passwd, compute a PHP-FPM pool port as "1" + the user's UID, sed-substitute {vhost} / {user} / {pool_port} into the template, move the result into sites-available/NAME, then optionally call en NAME and optionally splice a 127.0.0.1 NAME mapping into /etc/hosts.
-l | List enabled vhosts (ls $NGINX_DIR/sites-enabled) and return — short-circuits before generation |
-u USER | Set the owning user (default $USER); used for the web root and fastcgi socket, looked up in /etc/passwd |
-t TPL | Template to use (default $NGINX_VHOST_TEMPLATE); searched under $ZSH/plugins/nginx/templates/$TPL first, then accepted as a raw path |
-n | Generate only — skip the trailing en auto-enable |
-w | Write a 127.0.0.1 NAME entry into /etc/hosts for local-only resolution |
-h | Print the usage banner and return |
Completion
Each function ships a paired #compdef completion. Candidates come from live filesystem state — ls -a over the relevant directory piped through an awk filter — not a hand-maintained list, so a site added by hand outside the plugin still tab-completes.
en <TAB> | configs in $NGINX_DIR/sites-available (filter /^[a-z][a-z.-]+$/) |
dis <TAB> | configs in $NGINX_DIR/sites-enabled (filter /^[a-z][a-z.-]+$/) |
vhost <TAB> | project directories in $HOME/www (filter /^[^.][a-z0-9._]+$/) |
Templates
Two stock vhost templates ship in templates/, both targeting per-user PHP-FPM over a unix-socket fastcgi backend and both denying .ht* dotfiles. Substitution is a single sed pass over three placeholders: {vhost}, {user}, {pool_port}. To add your own, drop a file into $ZSH/plugins/nginx/templates/ and select it with vhost -t NAME.
symfony2 | Web root /home/{user}/www/{vhost}/web, front controller app_dev.php, fastcgi scoped to app/app_dev/check.php, client_max_body_size 10M, per-vhost error+access logs |
plain_php | Web root /home/{user}/www/{vhost}, front controller index.php, fastcgi scoped to any .php, per-vhost error+access logs |
Configuration
Two environment variables, both with : ${VAR:=default} defaults set at source time (existing values are preserved on re-source).
NGINX_DIR | Root for sites-available/ + sites-enabled/ — default /etc/nginx |
NGINX_VHOST_TEMPLATE | Default template when vhost is called without -t — default $ZSH/plugins/nginx/templates/symfony2 |
Examples
nginx <TAB> # nginx flags / signals (system completion)
ngt # test running config ($sudo nginx -t)
ngr # restart service ($sudo service nginx restart)
vhost foo # generate sites-available/foo from the default
# template and enable it
vhost foo -w # ...and also add "127.0.0.1 foo" to /etc/hosts
vhost foo -t plain_php # use the plain_php template instead
vhost foo -n # generate only, do not enable
vhost foo -u deploy # own the vhost as user "deploy"
vhost -l # list currently enabled vhosts
vhost -h # usage banner
en foo <TAB> # complete from sites-available
dis foo <TAB> # complete from sites-enabled
# full local-dev loop:
vhost foo -w && ngt && ngr
Troubleshooting
| "The name of the vhost is required!" | en / dis / vhost were called without a NAME argument |
| "NAME doesn't exist" | en — no matching config in $NGINX_DIR/sites-available; create it (e.g. with vhost) first |
| "NAME is already enabled" | en — the sites-enabled symlink already exists; nothing to do |
| "User USER doesn't have an account" | vhost — the -u user is not in /etc/passwd |
Permission denied on /etc/nginx | sudo was not found on PATH at source time, so the prefix is empty; run as root or install sudo |
ngr fails on a systemd box | redefine ngr to use systemctl restart nginx |
Deeper internals — LOC breakdown, the vhost pipeline diagram, test coverage and design decisions — are in the Engineering Report.
Sibling plugins
Part of the MenkeTechnologies zsh plugin family — the MenkeTechnologiesMeta umbrella:
- zsh-better-npm-completion — cache-aware
npm installcompletion - zsh-cargo-completion — live
cargo add/cargo installcompletion - zsh-cpan-completion — live MetaCPAN
cpan/cpanmcompletion - zsh-dotnet-completion — dotnet CLI completion
- zsh-expand — spacebar expansion (11,683 tests)
- zsh-gem-completion — ruby
gem installremote completion - zsh-git-acp — 159 git aliases + add/commit/push keybindings
- zsh-git-repo-cache — index every git repo on disk
- zsh-learn — MySQL-backed learning collection / quiz
- zsh-more-completions — 33,175-file mega completion corpus
- zsh-pip-description-completion — pip remote completion with descriptions
- zsh-sed-sub — in-place sed substitution on the command line
- zsh-sudo — ESC ESC to prepend sudo
- zsh-xcode-completions — xcodebuild / xcrun / swift completion