Vagrant Cheatsheet - VM Init, Up, SSH & Provisioning
Vagrant reduces to one promise: write how the environment is built into a Vagrantfile, and vagrant up reproduces the same machine on anyone laptop. The lifecycle commands are not the hard part. What actually breaks are three things: pinning the box version, choosing a synced folder type, and understanding exactly when provision scripts run. This table groups those configuration points alongside the lifecycle commands, covering box management, Vagrantfile settings, networking and synced folders, multi-machine definitions, and snapshots. Reach for it when inheriting a Vagrantfile that will not boot, when sharing an environment with a new teammate, when Windows file watching makes the default shared folder painfully slow, or when you need several machines up together for integration work. The test of a good definition is destructiveness: if destroy followed by up reproduces an equivalent machine, the setup is genuinely portable rather than accidentally working.
Typical Use Case
For teams that need a multi-machine environment on one laptop and want every member to reach the same system with a single command: a local LAMP stack, a multi-node load-balancing rehearsal, or a CI job needing a service dependency. The machine and its provisioning scripts both live in a reviewable Vagrantfile, so the key is four state lines: VM lifecycle, box management, networking and synced folders, and provisioning on up and reload. The command to fear is destroy.
VM Lifecycle 9
vagrant init ubuntu/jammy64vagrant upvagrant sshvagrant haltvagrant destroyvagrant suspend / vagrant resumevagrant reloadvagrant statusvagrant global-statusBox Management 6
vagrant box add ubuntu/jammy64vagrant box listvagrant box outdatedvagrant box update --box ubuntu/jammy64vagrant box remove ubuntu/jammy64vagrant box add my-box ./package.boxVagrantfile 6
config.vm.box = "ubuntu/jammy64"config.vm.hostname = "dev-server"config.vm.provider "virtualbox" do |vb|
vb.memory = "2048"
vb.cpus = 2
endconfig.vm.synced_folder "./src", "/var/www"config.vm.provision "shell", path: "setup.sh"config.vm.provision "shell", inline: "apt-get update"Network & Sync 6
config.vm.network "forwarded_port", guest: 80, host: 8080config.vm.network "private_network", ip: "192.168.33.10"config.vm.network "public_network"config.vm.synced_folder "./src", "/var/www", type: "nfs"config.vm.synced_folder "./data", "/data", disabled: truevagrant portMulti-Machine & Provision 6
config.vm.define "web" do |web|
web.vm.box = "ubuntu/jammy64"
endconfig.vm.define "db" do |db|
db.vm.box = "ubuntu/jammy64"
endvagrant up webvagrant ssh webvagrant provisionvagrant provision --provision-with shellSnapshots & Plugins 7
vagrant snapshot save my-snapshotvagrant snapshot restore my-snapshotvagrant snapshot listvagrant snapshot delete my-snapshotvagrant plugin install vagrant-vbguestvagrant plugin listvagrant package --output my-box.boxParameter matrix
| Parameter | Effect | Example |
|---|---|---|
vagrant init ubuntu/jammy64 | Writes a working Vagrantfile into the current directory; every later command depends on this file existing. | vagrant init ubuntu/jammy64 |
vagrant up | Creates and boots the machine. The first run downloads the box and provisions it, and every later boot runs provisioning again. | vagrant up |
vagrant ssh | Logs in as the vagrant account, which spares you from remembering a username and password. | vagrant ssh |
vagrant halt | Powers the machine off cleanly while keeping its disk and configuration. This is the right command for pausing work overnight. | vagrant halt |
vagrant destroy | Destroys the machine and its disk. It cannot be undone; the Vagrantfile survives but the box download and provisioning have to happen again. | vagrant destroy |
vagrant suspend / vagrant resume | Writes a memory snapshot to disk and suspends; resume brings the machine back exactly as it was. Faster than halt, but it consumes disk. | vagrant suspend |
vagrant reload | Restarts the machine and re-runs provisioning, the standard response after editing a provisioning script. | vagrant reload |
vagrant status | Shows the state of every machine in the project: running, poweroff or not created. | vagrant status |
vagrant global-status | Lists the machines of every project under the directory, which settles the "am I operating in the wrong folder" confusion. | vagrant global-status |
vagrant box add ubuntu/jammy64 | Caches a box locally, so other projects using the same box no longer download it. | vagrant box add ubuntu/jammy64 |
vagrant box list | Lists the locally cached boxes and their versions, which often explains a version mismatch. | vagrant box list |
vagrant box outdated | Reports whether cached boxes have newer versions available, the standard pre-upgrade check. | vagrant box outdated |
vagrant box remove ubuntu/jammy64 | Removes a cached box; if it refuses, check first whether a machine is still built from it. | vagrant box remove ubuntu/jammy64 |
config.vm.box | Selects which box to use. The version pin lives here, and changing it requires a destroy and rebuild to take effect. | config.vm.box = "ubuntu/jammy64" |
config.vm.hostname | Sets the hostname inside the machine, handy for confirming which box you are actually connected to. | config.vm.hostname = "dev-server" |
vb.memory / vb.cpus | Tunes memory and CPU inside the provider block; a reload is required before the new values apply. | config.vm.provider "virtualbox" do |vb|
vb.memory = "2048"
vb.cpus = 2
end |
config.vm.synced_folder "./src", "/var/www" | Mounts a host directory into the guest. The default VirtualBox shared folder is slow, so switch to nfs when performance matters. | config.vm.synced_folder "./src", "/var/www" |
type: "nfs" | Serves the mount over NFS instead, noticeably faster than shared folders on a Linux host. | config.vm.synced_folder "./src", "/var/www", type: "nfs" |
disabled: true | Leaves the directory unmounted by default, to be enabled on demand with vagrant up and the matching enable flag. | config.vm.synced_folder "./data", "/data", disabled: true |
config.vm.provision "shell", path: "setup.sh" | Points at an external script for initialisation, which stays maintainable once the script logic grows past a line or two. | config.vm.provision "shell", path: "setup.sh" |
config.vm.provision "shell", inline: "apt-get update" | Inlines a command directly, suitable for one-line initialisation steps. | config.vm.provision "shell", inline: "apt-get update" |
forwarded_port | Maps a host port onto a guest port, the standard way to reach a service running inside the machine. | config.vm.network "forwarded_port", guest: 80, host: 8080 |
private_network, ip: "192.168.33.10" | Assigns a fixed address on the private network; both machine-to-machine access and documented addresses depend on it. | config.vm.network "private_network", ip: "192.168.33.10" |
vagrant port | Reports the port actually assigned, which differs from the configuration when Vagrant picked a free port or the configured one was taken. | vagrant port |
config.vm.define "web" / "db" | Defines several machines so that later commands can address just one of them. | config.vm.define "web" do |web|
web.vm.box = "ubuntu/jammy64"
end |
vagrant up web | Starts only the named machine, which avoids booting the whole set in a multi-machine project. | vagrant up web |
vagrant ssh web | Logs into a specific machine; without the name a multi-machine project logs into the wrong one. | vagrant ssh web |
vagrant provision | Re-runs the provisioning scripts without rebooting, which is the correct move after editing setup.sh. | vagrant provision |
vagrant provision --provision-with shell | Runs only the shell provisioners and skips the chef, puppet and other provisioners. | vagrant provision --provision-with shell |
vagrant snapshot save / restore / list / delete | Snapshots give finer-grained rollback points than boxes; taking one after dependencies are installed saves a great deal of reinstall time. | vagrant snapshot save my-snapshot |
vagrant plugin install | Installs a Vagrant plugin. Provider support for virtualbox or libvirt also arrives through plugins. | vagrant plugin install vagrant-vbguest |
vagrant plugin list | Lists installed plugins, the first place to look when a provider seems unavailable. | vagrant plugin list |
vagrant package --output my-box.box | Packages the current machine as a box the team can reuse, effectively a custom base image you own. | vagrant package --output my-box.box |
Common pitfalls
Symptomvagrant up reports that the box cannot be found, or boots a version other than the one expected.
CauseThe Vagrantfile names a floating version or a short name while the local cache holds a different version, and the two do not line up.
FixRun vagrant box list to see the version actually cached, pin the Vagrantfile to that exact version, and add the box with vagrant box add if it is missing.
SymptomAfter vagrant destroy the next vagrant up takes forever and everything installed is gone.
Causedestroy wipes the virtual disk, so nothing that provisioning created survives; only the Vagrantfile remains.
FixTake a snapshot once dependencies are in place with vagrant snapshot save my-snapshot and roll back with vagrant snapshot restore rather than reinstalling.
SymptomCommands were changed inside setup.sh, but re-running vagrant up makes no difference.
Causeup merely boots an existing machine; editing the provisioning file does not by itself trigger a re-run, and a re-run is only safe if the script itself is idempotent.
FixRun vagrant provision or vagrant reload after editing, and write the script so it can be executed repeatedly, for example by asking package managers to converge on a desired state rather than installing unconditionally.
SymptomThe host can ping the private address, but machines inside the environment cannot reach each other.
CauseA private_network was configured without an ip, leaving the address uncertain, and the host firewall may not permit the subnet Vagrant chose.
FixPin an ip for every machine and confirm the host firewall allows 192.168.33.0/24.
SymptomThe synced folder appears empty inside the machine, or file access is painfully slow.
CauseThe host path is wrong or contains characters that break the mount, and the default shared folder is extremely slow in some setups.
FixCheck the mount inside the machine to confirm it really attached, and set type to "nfs" on a Linux host.
SymptomA forwarded port refuses connections from the host with connection refused.
CauseThe service listens on 127.0.0.1 rather than 0.0.0.0, so forwarded connections are rejected, or the port was already taken.
FixHave the service bind 0.0.0.0 and confirm the host port with vagrant port.
Symptomvagrant box remove reports that the box is still in use.
CauseA machine is still built from that box, and Vagrant refuses to remove one that is referenced.
FixRun vagrant status to find the projects involved, destroy those machines, then remove the box.
SymptomIn a multi-machine project vagrant ssh always lands on the same machine.
CauseWith no machine name given, Vagrant picks the first machine defined in the Vagrantfile.
FixAlways pass the name explicitly, as in vagrant ssh web, and cross-check each machine with vagrant status.
Troubleshooting
1Check the state of every machine in this directory
vagrant statusTelling not created, poweroff and running apart decides whether to bring a machine up or reload it.
2Check machine state across all projects
vagrant global-statusUse it to locate yourself whenever you suspect the current directory is not the right one.
3Verify cached boxes and their versions
vagrant box listA version mismatch is the first suspect when boot fails.
4Check whether a box upgrade is available
vagrant box outdatedBack up the Vagrantfile and the provisioning scripts first.
5Confirm installed plugins and provider support
vagrant plugin listCheck this before assuming anything about a missing provider.
6Look up the port actually assigned
vagrant portA value that differs from the host port in the Vagrantfile means Vagrant reallocated it automatically.
Command Examples
Bring up a dev box with a forwarded port
vagrant init ubuntu/jammy64
vagrant box add ubuntu/jammy64
vagrant up
vagrant port
vagrant sshvagrant init only writes the Vagrantfile; it does not download the image, so run vagrant box add explicitly. Vagrant may fetch it automatically when synced folders are configured, but adding it first avoids surprises. vagrant port tells you the mapping that actually took effect: when 8080 is already taken, VirtualBox silently shifts to the next port, so never assume the number in your config file.
Output
==> web: 80 (guest) => 8080 (host) ==> default: Running (virtualbox)
Provision is the idempotent entry point; re-run it while debugging
vagrant provision
vagrant provision --provision-with shell
vagrant reloadAfter editing setup.sh there is no need to destroy and up: vagrant provision re-runs the script and saves minutes of rebuild time. Shell provisions must be idempotent on their own — apt-get update always runs, and an unguarded >> appends the config a second time — because Vagrant only guarantees the script runs once, not that it is safe to run twice.
Snapshot before any destructive operation
vagrant status
vagrant snapshot save my-snapshot
vagrant snapshot list
vagrant snapshot restore my-snapshotSave a snapshot before destroy: recovery then means vagrant snapshot restore my-snapshot instead of starting over with a fresh vagrant init, up, and a full provision run. Snapshots live in the .vagrant directory, so make sure no cleanup script removes them — they can be copied along with the project directory.
Multi-machine: define them, then bring up a specific one
vagrant status
vagrant up web
vagrant ssh web
vagrant global-statusTwo config.vm.define blocks live in the same Vagrantfile, and a bare vagrant up starts every machine, doubling memory and port usage. Always name the machine explicitly when you only need the web box. vagrant global-status shows every VM across every project on the host, which makes it the first place to look when a box "is up but unreachable".
Output
==> web: machine => web ==> db: machine => db
Drop the default synced folder in favour of a fixed-IP direct route
config.vm.synced_folder "./data", "/data", disabled: true
config.vm.network "private_network", ip: "192.168.33.10"
vagrant reloadShared folders provided by the hypervisor are far slower than native host IO for build-heavy work. The common pattern is disabled: true on the default /vagrant mount plus a private_network fixed IP, so the host reads and writes the guest directory directly. A vagrant reload is required for the network change to take effect.
Common Pitfalls
- vagrant destroy cannot be undone: the virtual disk, everything provisioned, databases, and keys inside are gone. Source survives if the default synced_folder mount exists, but apt packages, database files, and manually created users inside the VM are destroyed with it. Always vagrant snapshot save first, or vagrant package --output backup.box to keep a copy.
- vagrant halt only powers the machine off (state: poweroff) while vagrant destroy deletes it. Both make the box "disappear", but halt can be undone with vagrant up and keeps its data. When someone says they "shut it down", confirm they are not about to run destroy.
- A synced_folder mounts the entire host project directory read-write inside the VM. Any code running there, including postinstall scripts of dependencies or a Makefile target, can rewrite your host sources. On untrusted environments or unknown projects, think hard about that mount direction before starting.
- A forwarded_port whose host port is already taken does not error; VirtualBox silently moves to the next free port and vagrant up still reports success. A CI script hardcoding http://localhost:8080 then talks to the wrong thing or nothing. Read the real mapping with vagrant port instead of hardcoding it.
- vagrant box remove deletes the global box cache (by default under ~/.vagrant.d/boxes). Removing a shared team image means every machine re-downloads hundreds of megabytes to several gigabytes on the next vagrant up, which simply hangs offline. Check vagrant box list and tell the team first.
- vagrant package captures the full current state, including all data, tokens, .env files, and private keys (unattended VirtualBox generates authorized_keys inside the machine). Never upload the resulting .box to a public location or commit it; strip sensitive data first.
Tips
- vagrant up downloads the box on first run; later starts are fast.
- Vagrantfile is Ruby, but only basic config is needed.
- vagrant-vbguest auto-updates VirtualBox Guest Additions.
- snapshot save/restore quickly persists/restores VM state.
- NFS sync outperforms the default VBox shared folder for big projects.
- vagrant package bundles a configured VM into a box for sharing.
FAQ
Should I reload or destroy the VM after installing new dependencies?
Prefer vagrant reload: it restarts the machine and re-reads the Vagrantfile while keeping the data on disk. To re-run provisioning scripts, use vagrant provision, or scope it to one provisioner with --provision-with shell. vagrant suspend and vagrant resume are the fastest pair for closing the laptop and picking work back up. vagrant destroy wipes the machine and every change in it, so reserve it for deliberate clean slates. For a read-only check use vagrant status, or vagrant global-status to see every Vagrant environment on the machine at once.
Why is a prepared VM so much faster than the base box, and what does it cost me?
The first vagrant up downloads the box image and runs a full provision pass that installs dependencies and initialises the machine; every later boot reuses that on-disk state and is ready in seconds. The cost is a slow first run plus a locally dirty environment: after editing a template you must reload or re-provision manually. When repeated rebuilds get expensive, store a clean baseline with vagrant snapshot save my-snapshot and return to it any time via vagrant snapshot restore my-snapshot. Review them with vagrant snapshot list and delete the ones you no longer need.
How do I define multiple machines (web and db) in the Vagrantfile?
Name each machine with config.vm.define: config.vm.define "web" do |web| web.vm.box = "ubuntu/jammy64" end and config.vm.define "db" do |db| db.vm.box = "ubuntu/jammy64" end. Then vagrant up web boots just that machine and vagrant ssh web logs into it, while omitting the name starts everything. Cross-machine access is handled by the network config: config.vm.network "forwarded_port", guest: 80, host: 8080 publishes a guest port on the host, and vagrant port shows the active mapping. For a stable address use config.vm.network "private_network", ip: "192.168.33.10".
Installing dependencies over the shared folder is painfully slow — what about NFS?
The default VirtualBox shared folder performs badly with many small files, exactly what npm install and pip install generate. Switching with config.vm.synced_folder "./src", "/var/www", type: "nfs" routes traffic through NFS and is typically far quicker. Trim the surface with config.vm.synced_folder "./data", "/data", disabled: true so large trees are not mounted at all. Size the machine itself in the same config.vm.provider "virtualbox" block: vb.memory = "2048" and vb.cpus = 2, so the VM has enough headroom before you install anything. You can also run vagrant plugin install vagrant-vbguest to keep Guest Additions current.
How do I hand my tuned VM to the team instead of making everyone reinstall?
Export it: vagrant package --output my-box.box bundles the current VM into a box file, and a teammate installs it locally with vagrant box add my-box ./package.box before pointing their project at it. Tidy your box inventory first with vagrant box list, check vagrant box outdated for newer versions, refresh one with vagrant box update --box ubuntu/jammy64, and reclaim space with vagrant box remove for anything unused. Keep the Vagrantfile and provision scripts under version control as the source of truth — the packaged box is a snapshot of the environment, and both together are what guarantee your colleague gets the same setup.
Official References
Each command links to its official documentation below, so you can verify the latest usage and read deeper.
Maintained by LaoHand
Publicly updated on Jul 21, 2026, continuously proofread against official docs.
Contact Us
Wrong command or description? Send us corrections, business inquiries or product feedback by email.
Contact Us