Well, the extra bells and whistles is called process isolation, a container gets its own namespace from the host kernel, that means the program in the container can't try to read kernel memory or eat more RAM than allowed.
It also isolate network stacks, so two process can listen on port 8080 for exemple,
you'll have to handle the routing at host level, there's no magic here, but this allow handling the routing at one place and avoid modifying the process configuration to listen to a free port.
Secondly a chroot is still read/write, any change is permanent, a docker container using aufs
will start from a clean filesystem each time you launch the container (changes are kept if you stop/start it IIRC).
So while a container may be thought of as process namespace
+ chroot
, the reality is a little more complex.