diff --git a/playbooks/roles/edxapp/defaults/main.yml b/playbooks/roles/edxapp/defaults/main.yml
index ed15897..f672f03 100644
--- a/playbooks/roles/edxapp/defaults/main.yml
+++ b/playbooks/roles/edxapp/defaults/main.yml
@@ -86,6 +86,16 @@ EDXAPP_LMS_BASIC_AUTH: False
 EDXAPP_CMS_BASIC_AUTH: False
 EDXAPP_LMS_PREVIEW_BASIC_AUTH: False
 
+# whether to setup the python sandbox or not
+EDXAPP_PYTHON_SANDBOX: false
+EDXAPP_SANDBOX_VENV_DIR:  '/opt/edx-sandbox'
+EDXAPP_SANDBOX_USER: 'sandbox'
+EDXAPP_SANDBOX_GROUP: 'sandbox'
+# this next setting, if true, turns on actual sandbox enforcement.  If not true,
+# it puts the sandbox in 'complain' mode, for reporting but not enforcement
+EDXAPP_SANDBOX_ENFORCE: true
+EDXAPP_USER: 'www-data'
+COMMON_WEB_GROUP: 'www-data'
 
 #-------- Everything below this line is internal to the role ------------
 
@@ -252,6 +262,8 @@ lms_preview_env_config:
 # install dir for the edx-platform repo
 edx_platform_code_dir: "{{ app_base_dir }}/edx-platform"
 
+# sandbox command
+edxapp_aa_command: "{% if EDXAPP_SANDBOX_ENFORCE %}aa-enforce{% else %}aa-complain{% endif %}"
 
 # gunicorn ports/hosts, these shouldn't need to be overridden
 edxapp_cms_gunicorn_port: 8010
@@ -274,6 +286,8 @@ service_variants_enabled:
 
 edxapp_lms_env: 'lms.envs.aws'
 
+edxapp_user: '{{ EDXAPP_USER }}'
+common_web_group: '{{ COMMON_WEB_GROUP }}'
 
 #Number of gunicorn worker processes to spawn, as a multiplier to number of virtual cores
 worker_core_mult:
@@ -304,9 +318,6 @@ sandbox_base_requirements:  "{{ edx_platform_code_dir }}/requirements/edx-sandbo
 sandbox_local_requirements: "{{ edx_platform_code_dir }}/requirements/edx-sandbox/local.txt"
 sandbox_post_requirements:  "{{ edx_platform_code_dir }}/requirements/edx-sandbox/post.txt"
 
-#do we want to install the sandbox requirements into the regular virtual env
-install_sandbox_reqs_into_regular_venv: true
-
 lms_debian_pkgs:
   # for compiling the virtualenv
   # (only needed if wheel files aren't available)
diff --git a/playbooks/roles/edxapp/tasks/deploy.yml b/playbooks/roles/edxapp/tasks/deploy.yml
index ba35d6c..4ff6f12 100644
--- a/playbooks/roles/edxapp/tasks/deploy.yml
+++ b/playbooks/roles/edxapp/tasks/deploy.yml
@@ -112,6 +112,7 @@
   command: |
     /bin/sed -i -e 's/github\.com/{{ GIT_MIRROR }}/g' {{ item }}
   with_items:
+    - "{{ base_requirements_file }}"
     - "{{ pre_requirements_file }}"
     - "{{ post_requirements_file }}"
     - "{{ repo_requirements_file }}"
@@ -191,7 +192,7 @@
   - "{{ sandbox_base_requirements }}"
   - "{{ sandbox_local_requirements }}"
   - "{{ sandbox_post_requirements }}"
-  when: install_sandbox_reqs_into_regular_venv
+  when: not EDXAPP_PYTHON_SANDBOX
   tags:
   - lms
   - cms
@@ -200,6 +201,51 @@
 
 - name: changing group ownership to www-data for everything in the venv (workaround)
   shell: chgrp -R www-data {{ venv_dir }}
+  tags:
+  - deploy
+
+# need to disable this profile, otherwise the pip inside the sandbox venv has no permissions
+# to install anything
+- name: disable apparmor profile for code sandbox
+  shell: "{{ item }}"
+  with_items:
+    - "ln -s /etc/apparmor.d/code.sandbox /etc/apparmor.d/disable/"
+    - "apparmor_parser -R /etc/apparmor.d/code.sandbox"
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - deploy
+  - edxapp-sandbox
+
+- name: Install sandbox requirements into sandbox venv
+  shell: >
+    cd {{ edx_platform_code_dir }} &&
+    {{ EDXAPP_SANDBOX_VENV_DIR }}/bin/pip install -i {{ PYPI_MIRROR_URL }} --exists-action w --use-mirrors
+    --upgrade --no-deps -r {{ item }}
+  with_items:
+  - "{{ sandbox_base_requirements }}"
+  - "{{ sandbox_local_requirements }}"
+  - "{{ sandbox_post_requirements }}"
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - deploy
+  - edxapp-sandbox
+
+- name: re-enable apparmor profile for code sandbox
+  shell: "{{ item }}"
+  with_items:
+    - "unlink /etc/apparmor.d/disable/code.sandbox"
+    - "apparmor_parser -r /etc/apparmor.d/code.sandbox"
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - deploy
+  - edxapp-sandbox
+
+- name: put code sandbox into aa-enforce or aa-complain mode, depending on EDXAPP_SANDBOX_ENFORCE
+  command: /usr/sbin/{{ edxapp_aa_command }} /etc/apparmor.d/code.sandbox
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - deploy
+  - edxapp-sandbox
 
 # https://code.launchpad.net/~wligtenberg/django-openid-auth/mysql_fix/+merge/22726
 # This is necessary for when syncdb is run and the django_openid_auth module is installed,
diff --git a/playbooks/roles/edxapp/tasks/main.yml b/playbooks/roles/edxapp/tasks/main.yml
index 656dd36..2036890 100644
--- a/playbooks/roles/edxapp/tasks/main.yml
+++ b/playbooks/roles/edxapp/tasks/main.yml
@@ -50,6 +50,74 @@
   - upstart
   - update
 
+- name: Create edxapp sandbox group
+  group: name={{ EDXAPP_SANDBOX_GROUP }}
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+- name: Create edxapp sandbox user
+  user: name={{ EDXAPP_SANDBOX_USER }} group={{ EDXAPP_SANDBOX_GROUP }} shell=/bin/false home={{ EDXAPP_SANDBOX_VENV_DIR }}
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+- name: Create edxapp sandbox virtual env directory
+  file: >
+    path="{{ EDXAPP_SANDBOX_VENV_DIR }}"
+    state=directory
+    owner=root
+    group=root
+    mode=0755
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+- name: Create edxapp sandbox virtualenv
+  command: /usr/local/bin/virtualenv {{ EDXAPP_SANDBOX_VENV_DIR }} --distribute creates={{ EDXAPP_SANDBOX_VENV_DIR }}/bin/activate
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+- name: Install apparmor system pkg
+  apt: pkg=apparmor-utils state=present
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+- name: write out apparmor code sandbox config
+  template: src=code.sandbox.j2 dest=/etc/apparmor.d/code.sandbox mode=0644 owner=root group=root
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+- name: write out sandbox user sudoers config
+  template: src=95-sandbox-sudoer.j2 dest=/etc/sudoers.d/95-{{ EDXAPP_SANDBOX_USER }} mode=0440 owner=root group=root
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+# we boostrap and enable the apparmor service here.  in deploy.yml we disable, deploy, then re-enable
+# so we need to enable it in main.yml
+- name: start apparmor service
+  service: name=apparmor state=started
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+- name: (bootstrap) load code sandbox profile
+  command: apparmor_parser -r /etc/apparmor.d/code.sandbox
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+- name: (bootstrap) put code sandbox into aa-enforce or aa-complain mode depending on EDXAPP_SANDBOX_ENFORCE
+  command: /usr/sbin/{{ edxapp_aa_command }} /etc/apparmor.d/code.sandbox
+  when: EDXAPP_PYTHON_SANDBOX
+  tags:
+  - edxapp-sandbox
+
+
 - include: npm.yml
 - include: ruby.yml
 - include: deploy.yml
diff --git a/playbooks/roles/edxapp/templates/95-sandbox-sudoer.j2 b/playbooks/roles/edxapp/templates/95-sandbox-sudoer.j2
new file mode 100644
index 0000000..4920211
--- /dev/null
+++ b/playbooks/roles/edxapp/templates/95-sandbox-sudoer.j2
@@ -0,0 +1,3 @@
+{{ EDXAPP_USER }} ALL=({{ EDXAPP_SANDBOX_USER }})  SETENV:NOPASSWD:{{ EDXAPP_SANDBOX_VENV_DIR }}/bin/python
+{{ EDXAPP_USER }} ALL=(ALL) NOPASSWD:/bin/kill
+{{ EDXAPP_USER }} ALL=(ALL) NOPASSWD:/usr/bin/pkill
diff --git a/playbooks/roles/edxapp/templates/code.sandbox.j2 b/playbooks/roles/edxapp/templates/code.sandbox.j2
new file mode 100644
index 0000000..4e8dea3
--- /dev/null
+++ b/playbooks/roles/edxapp/templates/code.sandbox.j2
@@ -0,0 +1,28 @@
+#include <tunables/global>
+
+{{ EDXAPP_SANDBOX_VENV_DIR }}/bin/python flags=(complain) {
+    #include <abstractions/base>
+
+    {{ EDXAPP_SANDBOX_VENV_DIR }}/** mr,
+    {{ edx_platform_code_dir }}/common/lib/sandbox-packages/** r,
+    /tmp/codejail-*/ rix,
+    /tmp/codejail-*/** rix,
+
+    #
+    # Whitelist particiclar shared objects from the system
+    # python installation
+    #
+    /usr/lib/python2.7/lib-dynload/_json.so mr,
+    /usr/lib/python2.7/lib-dynload/_ctypes.so mr,
+    /usr/lib/python2.7/lib-dynload/_heapq.so mr,
+    /usr/lib/python2.7/lib-dynload/_io.so mr,
+    /usr/lib/python2.7/lib-dynload/_csv.so mr,
+    /usr/lib/python2.7/lib-dynload/datetime.so mr,
+    /usr/lib/python2.7/lib-dynload/_elementtree.so mr,
+
+    #
+    # Allow access to selections from /proc
+    #
+    /proc/*/mounts r,
+
+}
\ No newline at end of file