# Deploy to production server on shared hosting with gulp with FTP

**URL:** https://discourse.roots.io/t/deploy-to-production-server-on-shared-hosting-with-gulp-with-ftp/4435
**Category:** sage
**Tags:** gulp
**Created:** 2015-08-07T15:06:54Z
**Posts:** 7

## Post 1 by @Twansparant — 2015-08-07T15:06:54Z

Hi there,

Since most of my clients choose for a cheaper shared hosting solution and therefore I never have SSH access on the production server, I added **ftpush** to my **grunt build** command in the previous roots versions: [Add ftpush to Grunt Watch?](https://discourse.roots.io/t/add-ftpush-to-grunt-watch/889)

Now with the new Sage I’m looking for the same setup in **Gulp**. I installed and configured everything with the suggested **Bedrock & Trellis** setup before realizing I still need SSH access to actually deploy to remote servers.

I looked at **gulp-deploy-ftp** : [gulp-deploy-ftp - npm](https://www.npmjs.com/package/gulp-deploy-ftp/)  
and **vinyl-ftp** : [GitHub - morris/vinyl-ftp: Blazing fast vinyl adapter for FTP](https://github.com/morris/vinyl-ftp)

but was wundering if someone already has the same setup for hooking this into the `gulp` or `gulp --production` command and if it’s stable enough in practice?

Thanks a lot!

---

## Post 2 by @Twansparant — 2015-08-10T08:44:14Z

Additional question;  
What advantages does a Bedrock stacked WP site have if you can’t deploy to a production server with Capistrano due to a lack of SSH access? And if I manually FTP my Bedrock structure over to my production server, which files and folders are actually being used? Are these enough:

**.env**  
**config**  
**web**

Thanks!

---

## Post 3 by @Twansparant — 2015-08-10T10:19:39Z

Vinyl-ftp seems to work just fine, with the following additions to my **gulpfile.js** :

```
// ## Globals
...
var gutil = require('gulp-util');
var ftp = require('vinyl-ftp');

...
// ### Vinyl FTP
gulp.task('deploy', function() {
  var conn = ftp.create( {
    host: 'ftp.mydomain.com',
    user: 'username',
    password: 'password',
    parallel: 10,
    log: gutil.log
  });
  var globs = [
    '*',
    '*.php',
    'dist/**',
    'lang/**',
    'templates/*.php',
    'lib/*.php',
    '!.git',
    '!*.json',
    '!*.md',
    '!*.xml',
    '!assets',
    '!bower_components',
    '!dist/scripts/jquery.js',
    '!dist/scripts/jquery.js.map',
    '!dist/scripts/main.js',
    '!dist/scripts/main.js.map',
    '!dist/scripts/modernizr.js',
    '!dist/scripts/modernizr.js.map',
    '!dist/styles/editor-style.css',
    '!dist/styles/editor-style.css.map',
    '!dist/styles/main.css',
    '!dist/styles/main.css.map',
    '!gulpfile.js',
    '!node_modules',
    '!node_modules/**',
  ];
  // using base = '.' will transfer everything to /public_html correctly
  // turn off buffering in gulp.src for best performance
  return gulp.src( globs, { base: '.', buffer: false } )
    .pipe( conn.newer( '/public_html/web/app/themes/sage' ) ) // only upload newer files
    .pipe( conn.dest( '/public_html/web/app/themes/sage' ) );
});

// ### Build
// `gulp build` - Run all the build tasks but don't clean up beforehand.
// Generally you should be running `gulp` instead of `gulp build`.
gulp.task('build', function(callback) {
  runSequence('styles',
              'scripts',
              ['fonts', 'images'],
              'deploy',
              callback);
});
```

Since the deploy task also runs on the `gulp` command, I had to manually exclude the static and maps files in the **dist** folder to prevent them from being uploaded.

---

## Post 4 by @jakus — 2015-09-07T05:29:14Z

Hey Twansparant,

Thanks for your sharings on this! If I want to just run gulp for the local files without a deployment, what task would I run?

As I don’t really want to upload all files each time I run gulp… Or once I’ve done the initial upload will it just upload changed files?

Cheers,

J

---

## Post 5 by @Twansparant — 2015-09-07T08:58:17Z

> [@jakus](#):
>
> Thanks for your sharings on this! If I want to just run gulp for the local files without a deployment, what task would I run?

Good question, which I haven’t figured out yet unfortunately…  
In previous versions, you just ran `gulp build` but in Sage just `gulp` also runs the **build** task.

> [@jakus](#):
>
> As I don’t really want to upload all files each time I run gulp… Or once I’ve done the initial upload will it just upload changed files?

It does yes with the following line:

```
.pipe( conn.newer( '/public_html/web/app/themes/sage' ) )
```

However the problem is that the entire **dist** folder gets rebuild on `gulp --production` which causes the entire folder to get re-uploaded on every build.

Another thing I just bumped into, is that the previous hashed versions of the minified files don’t get deleted from the ftp server. So I tried to add the following line which should delete the remote **dist** folder entirely before uploading:

```
.pipe( conn.rmdir( '/domains/mydomain.com/public_html/web/app/themes/sage/dist' ), cb )
```

Which does delete the folder, but it gives me a bunch of errors too:

```
[10:28:57] TypeError: Cannot read property 'on' of undefined
[10:28:57] TypeError: Cannot read property 'on' of undefined
/pathto/site/web/app/themes/sage/node_modules/vinyl-ftp/lib/delete.js:53
                        cb( err );
                        ^
TypeError: undefined is not a function
```

So I placed a callback function within the task:

```
gulp.task('upload', function(cb) {
  cb();
  ...
});
```

But that doesn’t work. I’m not that familiar (yet) with the gulp syntax.  
Where should I place the callback and what should go in it?

Could someone give us a hand?  
Thanks!

---

## Post 6 by @jakus — 2015-09-15T01:41:15Z

No one appears to have a hand available :frowning:

---

## Post 7 by @Twansparant — 2015-09-16T13:51:52Z

Solution: [Run task only on gulp --production](https://discourse.roots.io/t/run-task-only-on-gulp-production/4726/7?u=twansparant)
