Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Allow VM serial ports #536

hedgehog opened this Issue · 12 comments

4 participants


Issue #391 and related suggest there is some difference in running vm headless or with gui enabled.

When running VMs in headless/vrdp mode it is important to have the kernel messages piped to a local file on the host.
See references for howtos.

In a Vagrantfile it will help to have:

cfg.vm.serial_ports << {:port_number => 1|2|3|4,  :mode => :host_file|etc., :path => '/tmp/vboxconsole'}
cfg.vm.serial_ports << {:port_number => 1|2|3|4,  :mode => :host_file|etc., :path => '/tmp/vboxconsole2'}

Once implemented/rejected please note on Vewee issue #140.



Mitchell, I think this would open a robust way of configuring the VM. Essentially, replace the methods prepare_host_only_network, enable_host_only_network, change_host_name, etc. with a chef/puppet recipe that sets this up in one call. At the moment the initial setup is extremely brittle - in rewriting the ssh code I've encountered a few things each of which might have triggered the behaviour seen in issue #391, issue #455 et. al. Essentailly I've tried to rip them out, but some you can't right now.


In light of issue #482, it is better to use the shell provisioner to do the initial bootstrap over the serial console.


I think this should be exposed, but not as a top-level Vagrantfile option. I'd be much more comfortable with this as a config.vm.define type of option. That said, if the virtualbox gem doesn't support enabling serial ports, it should, and if it doesn't, it is a bug there.

I agree the networking calls are brittle, but I missed how serial ports help this?


Serial ports would help in that you'd execute the VM set up over a console rather than ssh.
It would require some minor grub changes to the base boxes, all simple and widely documented for the different distros. One major advantage is built in easy debugging of kernel and, yes, network issues such as #391, #410, #455, etc. :)



Could you expand a bit on the grub changes necessary to the base boxes?



This example does a lot more, but you can see the serial console parts:

        # If you change this file, run 'update-grub' afterwards to update
        # /boot/grub/grub.cfg.
        # SEE (Open at this time)

        GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
        GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0,115200n8 ignore_loglevel nomodeset nosplash noplymouth text INIT_VERBOSE=yes init=/sbin/init -v"

        # Uncomment to disable graphical terminal (grub-pc only)
        GRUB_TERMINAL_INPUT="serial console"
        GRUB_SERIAL_COMMAND="serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1"


        # ttyS0 - getty
        # This service maintains a getty on ttyS0 from the point the system is
        # started until it is shut down again.
        start on stopped rc or RUNLEVEL=[2345]
        stop on runlevel [!2345]

        exec /sbin/getty -L -n -i -l /bin/bash 115200 ttyS0 vt102

While it is fresh in my mind...
Some things to implement:

  • Open all 4 serial consoles by default, then this can be hidden from the user, and by default there is headroom, this is assuming Vagrant's target audience are dev's who regularly need more than the minimal :)
  • Drop-in starting point is the Event Machine+serialport snippet 'EM over a Serial Port'[1] and [2]. Other code/ideas[3].
  • bootstrap.erb script templates. Hopefully then the serial console work would involve just sending the contents read from such a template.




Hi Guys,

I've spiked tailing the kernels serial console output. To work it will likely require changes to both vagrant and the virtualbox gem. From what I can tell the LAST_LINE constant appears to be what is visible on the console when booting is complete and the OS has settled. This seems the safest time (although possibly not the fastest) to run provisioning, would you agree? Some distros automatic updates may cause delays at this point if there are locks on the package management database though. Anyway I have some more points but, it's late and I'm crossing the pacific tomorrow. :$


require 'rubygems'
require 'virtualbox'

# TODO: Add usage prompt if args are not specified.

# requires the following changes to your Guest VM
# /boot/grub/menu.lst;
#  Append to the end of your kernel line; 
#   console=ttyS0 console=tty0 ignore_loglevel
# Modify your VM's settings in VirtualBox (ensure you don't destroy it)
# 1) Goto your VM's settings window.
# 2) Goto the "Ports" tab.
# 3) Goto the "Serial Ports" sub-tab.
# 4) Ensure "Enable Serial Port" is checked.
# 5) Set "Port Number" to "COM1".
# 6) Set "Port Mode" to "Raw File".
# 7) Set "Port/File Path" to $SOME_VALID_FILEPATH (e.g. /tmp/tty0.serial)
# 8) Profit?!? No it isn't that the script.

VM_NAME='src_1324283138' # TODO: change to ARGV[0]
SERIAL_FILE='/tmp/tty0.serial' # TODO: change to ARGV[1]
LAST_LINE='vboxguest: Successfully loaded version 4.1.4 (interface 0x00010004)'

def touch(file), 'w+') { |f| }

vm = VirtualBox::VM.find(VM_NAME)
puts "Waiting for VM to boot..."

# VirtualBox zeros the file no point in getting stats before startup
start_stat = previous_stat = File.stat(SERIAL_FILE)
sleep_count = 0

while true do
  sleep 1
  current_stat = File.stat(SERIAL_FILE)
  sleep_count += 1
  break if sleep_count == MAX_SLEEP_COUNT && start_stat.mtime < current_stat.mtime
  next if current_stat.mtime == previous_stat.mtime
  sleep_count = 0
  previous_stat = current_stat

# just oversizing as I'm not certain how long the last may actually be with various versions of VirtualBox.
last_line_length = 2 * LAST_LINE.size
last_line_seek_position = File.size(SERIAL_FILE) - last_line_length - 1
last_lines =, last_line_seek_position, last_line_seek_position)
last_line = nil
last_lines.each_line { |l| last_line = l }

puts "Jobs Done!"
puts last_line

I've now abstracted out "communications" so that it would be trivial to add a serial port communication.

I'm not interested in doing this at the moment, but welcome any contributions.


@mitchellh mitchellh closed this

For reference - to use the serial console you can use

config.vm.define :base6 do |base_config|
base_config.vm.customize [
"modifyvm", :id, "--name", "CentOS 6 x86_64 Base",
"--uart1", "0x3F8", "4", "--uartmode1", "file", "/tmp/base6-console.log"


@vStone, thanks. Any idea how to execute commands on the vm in that setup?


@hedgehog I've been able to issue commands via the serial console but, I did it the lame way and configured it through the UI. I'll take a look at how to configure it via the command line this evening.

The script I used to launch the VM is here;

My configuration in the UI was as follows;

enable Port 1
Port Number = COM1
IRQ = 4
IO Port = 0x3F8
Port Mode = Host Pipe
Create Pipe = false
Port/File Path: /tmp/Centos5.7_com1

My menu.lst now looks as follows;

# grub.conf generated by anaconda
# Note that you do not have to rerun grub after making changes to this file
# NOTICE:  You have a /boot partition.  This means that
#          all kernel and initrd paths are relative to /boot/, eg.
#          root (hd0,0)
#          kernel /vmlinuz-version ro root=/dev/VolGroup00/LogVol00
#          initrd /initrd-version.img
serial --unit=0 --speed=9600 --word=8 --parity=no --stop=1
terminal serial console
title CentOS (2.6.18-274.18.1.el5)
    root (hd0,0)
    kernel /vmlinuz-2.6.18-274.18.1.el5 ro root=/dev/VolGroup00/LogVol00 console=tty0 console=ttyS0,9600n8
    initrd /initrd-2.6.18-274.18.1.el5.img
title CentOS (2.6.18-274.18.1.el5) no serial
    root (hd0,0)
    kernel /vmlinuz-2.6.18-274.18.1.el5 ro root=/dev/VolGroup00/LogVol00 console=ttyS0 console=tty0 ignore_loglevel
    initrd /initrd-2.6.18-274.18.1.el5.img
title CentOS (2.6.18-274.el5)
    root (hd0,0)
    kernel /vmlinuz-2.6.18-274.el5 ro root=/dev/VolGroup00/LogVol00
    initrd /initrd-2.6.18-274.el5.img
# == EOF ==
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.