By default, the ManualWorkflowSchedulerService is used without active timers. So when the delay is hit, the workflow yields the thread back to the scheduler, and when the delay expires, nobody is around to call RunWorkflow. If you look in the web.config for the service you should see the entry for the scheduler service and you can add in the UseActiveTimers=true which will cause the workflow to resume when the delay expires
<add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=22.214.171.124, Culture=neutral, PublicKeyToken=31bf3856ad364e35" useActiveTimers="true"/>
Boolean that determines how delay activities are handled. If true, the scheduler service automatically resumes workflows after delay activities expire (by using an in-memory timer). If false, the host must manually resume the workflow after the delay activities expire.